Hi, i am trying to cacche maps to vbe available offline, i am using following qml app (modified example from docs):
/* Copyright 2020 Esri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
// You can run your app in Qt Creator by pressing Alt+Shift+R.
// Alternatively, you can run apps through UI using Tools > External > AppStudio > Run.
// AppStudio users frequently use the Ctrl+A and Ctrl+I commands to
// automatically indent the entirety of the .qml file.
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick.Controls.Material 2.1
import QtPositioning 5.3
import QtSensors 5.3
import ArcGIS.AppFramework 1.0
import Esri.ArcGISRuntime 100.9
//import Esri.ArcGISExtras 100.0
import "controls" as Controls
//------------------------------------------------------------------------------
App {
id: app
width: 414
height: 736
readonly property url outputTileCache: Qt.resolvedUrl("./../") + "TileCacheQml_%1.tpkx".arg(new Date().getTime().toString())
property string statusText: "Ready"
function units(value) {
return AppFramework.displayScaleFactor * value
}
property real scaleFactor: AppFramework.displayScaleFactor
property int baseFontSize : app.info.propertyValue("baseFontSize", 15 * scaleFactor) + (isSmallScreen ? 0 : 3)
property bool isSmallScreen: (width || height) < units(400)
Page{
anchors.fill: parent
header: ToolBar{
id:header
width: parent.width
height: 50 * scaleFactor
Material.background: "#8f499c"
Controls.HeaderBar{}
}
Text{
text:'hello world' + statusText
color:'white'
}
// sample starts here ------------------------------------------------------------------
contentItem: Rectangle{
anchors.top:header.bottom
MapView {
id: mapView
anchors.fill: parent
Component.onCompleted: {
// Set the focus on MapView to initially enable keyboard navigation
forceActiveFocus();
}
Map {
id: map
BasemapImageryWithLabelsVector {}
// Add an imagery basemap to the map and get the url of the raster baselayer once it has loaded
// Basemap {
// //initStyle: Enums.BasemapStyleArcGISImagery
// onLoadStatusChanged: {
// if (loadStatus !== Enums.LoadStatusLoaded)
// return;
// exportTask.url = baseLayers.get(0).url;
// }
// }
initialViewpoint: ViewpointCenter {
center: Point {
x: -117
y: 35
spatialReference: SpatialReference { wkid: 4326 }
}
targetScale: 1e7
}
}
}
// Create ExportTileCacheTask
//! [ExportTiles ExportTileCacheTask]
ExportTileCacheTask {
id: exportTask
property var exportJob
onCreateDefaultExportTileCacheParametersStatusChanged: {
if (createDefaultExportTileCacheParametersStatus === Enums.TaskStatusCompleted) {
params = defaultExportTileCacheParameters;
// export the cache with the parameters
executeExportTileCacheTask(params);
}
}
function generateDefaultParameters(param) {
// generate the default parameters with the extent and map scales specified
let res = createDefaultExportTileCacheParameters(param, mapView.mapScale, mapView.mapScale/10);
console.log('generated', res)
return res;
}
function executeExportTileCacheTask(params) {
// execute the asynchronous task and obtain the job
exportJob = exportTask.exportTileCache(params, outputTileCache);
// check if job is valid
if (exportJob) {
// show the export window
exportWindow.visible = true;
// connect to the job's status changed signal to know once it is done
exportJob.statusChanged.connect(updateJobStatus);
exportJob.progressChanged.connect(updateExportProgress);
exportJob.start();
} else {
exportWindow.visible = true;
statusText = "Export failed 2";
exportWindow.hideWindow(5000);
}
}
function updateJobStatus() {
switch(exportJob.jobStatus) {
case Enums.JobStatusFailed:
statusText = "Export failed 1";
exportWindow.hideWindow(5000);
break;
case Enums.JobStatusNotStarted:
statusText = "Job not started";
break;
case Enums.JobStatusPaused:
statusText = "Job paused";
break;
case Enums.JobStatusStarted:
statusText = "In progress...";
break;
case Enums.JobStatusSucceeded:
statusText = "Adding TPKX...";
exportWindow.hideWindow(1500);
displayOutputTileCache(exportJob.result);
break;
default:
break;
}
}
function updateExportProgress() {
exportTileCacheProgress = exportJob.progress;
}
function displayOutputTileCache(tileCache) {
// create a new tiled layer from the output tile cache
const tiledLayer = ArcGISRuntimeEnvironment.createObject("ArcGISTiledLayer", { tileCache: tileCache } );
// create a new basemap with the tiled layer
const basemap = ArcGISRuntimeEnvironment.createObject("Basemap");
basemap.baseLayers.append(tiledLayer);
// set the new basemap on the map
map.basemap = basemap;
// zoom to the new layer and hide window once loaded
tiledLayer.loadStatusChanged.connect(()=> {
if (tiledLayer.loadStatus === Enums.LoadStatusLoaded) {
extentRectangle.visible = false;
downloadButton.visible = false;
const prevMapScale = mapView.mapScale;
mapView.setViewpointScale(prevMapScale * .5);
map.minScale = prevMapScale;
map.maxScale = prevMapScale / 10;
}
});
}
Component.onDestruction: {
if (exportJob) {
exportJob.statusChanged.disconnect(updateJobStatus);
exportJob.progressChanged.disconnect(updateExportProgress);
}
}
}
//! [ExportTiles ExportTileCacheTask]
Rectangle {
id: extentRectangle
anchors.centerIn: parent
width: parent.width - (50)
height: parent.height - (125)
color: "transparent"
border {
color: "red"
width: 3
}
}
// Create the download button to export the tile cache
Rectangle {
id: downloadButton
property bool pressed: false
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
bottomMargin: 23
}
width: 130
height: 35
color: pressed ? "#959595" : "#D6D6D6"
radius: 5
border {
color: "#585858"
width: 1
}
Row {
anchors.fill: parent
spacing: 5
Text {
anchors.verticalCenter: parent.verticalCenter
text: "Export tiles"
font.pixelSize: 14
color: "#474747"
}
}
MouseArea {
anchors.fill: parent
onPressed: downloadButton.pressed = true
onReleased: downloadButton.pressed = false
onClicked: {
console.log('downloading')
let params = getRectangleEnvelope();
console.log('after')
// let params = exportTask.defaultExportTileCacheParameters;
console.log('executing', params)
// export the cache with the parameters`
function executeExportTileCacheTask(params) {
// execute the asynchronous task and obtain the job
console.log('tileCache:', app.outputTileCache)
console.log('params:', params)
let exportJob = exportTask.exportTileCache(params, outputTileCache);
console.log('export job:', exportJob);
// check if job is valid
if (exportJob) {
// show the export window
exportWindow.visible = true;
// connect to the job's status changed signal to know once it is done
exportJob.statusChanged.connect(updateJobStatus);
exportJob.progressChanged.connect(updateExportProgress);
exportJob.start();
} else {
exportWindow.visible = true;
statusText = "Export failed 3";
exportWindow.hideWindow(5000);
}
}
executeExportTileCacheTask(params);
}
function getRectangleEnvelope() {
const corner1 = mapView.screenToLocation(extentRectangle.x, extentRectangle.y);
const corner2 = mapView.screenToLocation((extentRectangle.x + extentRectangle.width), (extentRectangle.y + extentRectangle.height));
const envBuilder = ArcGISRuntimeEnvironment.createObject("EnvelopeBuilder");
envBuilder.setCorners(corner1, corner2);
console.log('before start');
// tileCacheExtent = GeometryEngine.project(envBuilder.geometry, Factory.SpatialReference.createWebMercator());
return exportTask.generateDefaultParameters(GeometryEngine.project(envBuilder.geometry, Factory.SpatialReference.createWebMercator()));
// console.log('generated default parameteres');
}
}
}
// Create a window to display the export window
Rectangle {
id: exportWindow
anchors.fill: parent
color: "transparent"
visible: false
clip: true
Rectangle {
anchors.fill: parent
color: "#60000000"
}
MouseArea {
anchors.fill: parent
onClicked: mouse => mouse.accepted = true
onWheel: wheel => wheel.accepted = true
}
Rectangle {
anchors.centerIn: parent
width: 140
height: 145
color: "lightgrey"
opacity: 0.8
radius: 5
border {
color: "#4D4D4D"
width: 1
}
Column {
anchors {
fill: parent
margins: 10
}
spacing: 10
BusyIndicator {
anchors.horizontalCenter: parent.horizontalCenter
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: statusText
font.pixelSize: 16
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: 0/*exportTileCacheProgress*/ + "% Completed"
font.pixelSize: 16
}
}
}
Timer {
id: hideWindowTimer
onTriggered: exportWindow.visible = false;
}
function hideWindow(time) {
hideWindowTimer.interval = time;
hideWindowTimer.restart();
}
}
}
}
Controls.DescriptionPage{
id:descPage
visible: false
}
}
//------------------------------------------------------------------------------
when i rty to click download buttoin i always encounter error labeled "Export error 3", i also checked what is exportJob equal to after i run let exportJob = exportTask.exportTileCache(params, outputTileCache);
it is always null
what might i be doing wrong here?
It's difficult to say for sure what's going wrong. I'm guessing it's something to do with how you're creating the ExportTileCacheParameters, but I can't say for certain. Can you share your entire application output? It looks like you have a lot of things logged to the console that may help me understand. Additionally, can you try putting the following in your ExportTileCacheTask?
onErrorChanged: {
console.log("Export task error:", exportTask.error.message, exportTask.error.additionalMessage);
}
That may help illuminate the problem.
Looking forward to hearing back from you, thank you!