mirror of
https://github.com/Esri/offline-editor-js.git
synced 2025-12-15 15:20:05 +00:00
globally changed tabs to spaces
This commit is contained in:
parent
eea34cbcde
commit
ab140f1d45
@ -1,50 +1,50 @@
|
||||
"use strict";
|
||||
|
||||
define([
|
||||
"edit/editsStore",
|
||||
"dojo/Evented",
|
||||
"dojo/_base/Deferred",
|
||||
"dojo/promise/all",
|
||||
"dojo/_base/declare",
|
||||
"dojo/_base/lang",
|
||||
"esri/layers/GraphicsLayer",
|
||||
"esri/graphic",
|
||||
"esri/symbols/SimpleMarkerSymbol",
|
||||
"esri/symbols/SimpleLineSymbol",
|
||||
"esri/symbols/SimpleFillSymbol",
|
||||
"esri/request"],
|
||||
function(editsStore,Evented,Deferred,all,declare,lang,GraphicsLayer,Graphic,SimpleMarkerSymbol,SimpleLineSymbol,SimpleFillSymbol,esriRequest)
|
||||
"edit/editsStore",
|
||||
"dojo/Evented",
|
||||
"dojo/_base/Deferred",
|
||||
"dojo/promise/all",
|
||||
"dojo/_base/declare",
|
||||
"dojo/_base/lang",
|
||||
"esri/layers/GraphicsLayer",
|
||||
"esri/graphic",
|
||||
"esri/symbols/SimpleMarkerSymbol",
|
||||
"esri/symbols/SimpleLineSymbol",
|
||||
"esri/symbols/SimpleFillSymbol",
|
||||
"esri/request"],
|
||||
function(editsStore,Evented,Deferred,all,declare,lang,GraphicsLayer,Graphic,SimpleMarkerSymbol,SimpleLineSymbol,SimpleFillSymbol,esriRequest)
|
||||
{
|
||||
return declare([Evented],
|
||||
{
|
||||
_onlineStatus: "online",
|
||||
_featureLayers: {},
|
||||
return declare([Evented],
|
||||
{
|
||||
_onlineStatus: "online",
|
||||
_featureLayers: {},
|
||||
|
||||
ONLINE: "online", // all edits will directly go to the server
|
||||
OFFLINE: "offline", // edits will be enqueued
|
||||
RECONNECTING: "reconnecting", // sending stored edits to the server
|
||||
ONLINE: "online", // all edits will directly go to the server
|
||||
OFFLINE: "offline", // edits will be enqueued
|
||||
RECONNECTING: "reconnecting", // sending stored edits to the server
|
||||
|
||||
// manager emits event when...
|
||||
events: {
|
||||
EDITS_SENT: 'edits-sent', // ...whenever any edit is actually sent to the server
|
||||
EDITS_ENQUEUED: 'edits-enqueued', // ...when an edit is enqueued (and not sent to the server)
|
||||
ALL_EDITS_SENT: 'all-edits-sent' // ...after going online and there are no pending edits in the queue
|
||||
},
|
||||
// manager emits event when...
|
||||
events: {
|
||||
EDITS_SENT: 'edits-sent', // ...whenever any edit is actually sent to the server
|
||||
EDITS_ENQUEUED: 'edits-enqueued', // ...when an edit is enqueued (and not sent to the server)
|
||||
ALL_EDITS_SENT: 'all-edits-sent' // ...after going online and there are no pending edits in the queue
|
||||
},
|
||||
|
||||
/**
|
||||
* Overrides a feature layer.
|
||||
* @param layer
|
||||
* @returns deferred
|
||||
*/
|
||||
extend: function(layer)
|
||||
{
|
||||
var self = this;
|
||||
extend: function(layer)
|
||||
{
|
||||
var self = this;
|
||||
|
||||
// we keep track of the FeatureLayer object
|
||||
this._featureLayers[ layer.url ] = layer;
|
||||
// we keep track of the FeatureLayer object
|
||||
this._featureLayers[ layer.url ] = layer;
|
||||
|
||||
/* replace the applyEdits() method */
|
||||
layer._applyEdits = layer.applyEdits;
|
||||
/* replace the applyEdits() method */
|
||||
layer._applyEdits = layer.applyEdits;
|
||||
|
||||
/**
|
||||
* Overrides the ArcGIS API for JavaSccript applyEdits() method.
|
||||
@ -55,411 +55,411 @@ define([
|
||||
* @param errback An error object is returned if an error occurs
|
||||
* @returns {*} deferred
|
||||
*/
|
||||
layer.applyEdits = function(adds,updates,deletes,callback,errback)
|
||||
{
|
||||
// inside this method, 'this' will be the FeatureLayer
|
||||
// and 'self' will be the offlineFeatureLayer object
|
||||
layer.applyEdits = function(adds,updates,deletes,callback,errback)
|
||||
{
|
||||
// inside this method, 'this' will be the FeatureLayer
|
||||
// and 'self' will be the offlineFeatureLayer object
|
||||
|
||||
if( self.getOnlineStatus() == self.ONLINE)
|
||||
{
|
||||
var def = this._applyEdits(adds,updates,deletes,
|
||||
function()
|
||||
{
|
||||
self.emit(self.events.EDITS_SENT,arguments);
|
||||
callback && callback.apply(this,arguments);
|
||||
},
|
||||
errback);
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
var deferred = new Deferred();
|
||||
var results = { addResults:[],updateResults:[], deleteResults:[] };
|
||||
var updatesMap = {};
|
||||
if( self.getOnlineStatus() == self.ONLINE)
|
||||
{
|
||||
var def = this._applyEdits(adds,updates,deletes,
|
||||
function()
|
||||
{
|
||||
self.emit(self.events.EDITS_SENT,arguments);
|
||||
callback && callback.apply(this,arguments);
|
||||
},
|
||||
errback);
|
||||
return def;
|
||||
}
|
||||
else
|
||||
{
|
||||
var deferred = new Deferred();
|
||||
var results = { addResults:[],updateResults:[], deleteResults:[] };
|
||||
var updatesMap = {};
|
||||
|
||||
this.onBeforeApplyEdits(adds, updates, deletes);
|
||||
this.onBeforeApplyEdits(adds, updates, deletes);
|
||||
|
||||
adds = adds || [];
|
||||
adds.forEach(function(addEdit)
|
||||
{
|
||||
var objectId = this.getNextTempId();
|
||||
addEdit.attributes[ this.objectIdField ] = objectId;
|
||||
var result = editsStore.pushEdit(editsStore.ADD, this.url, addEdit);
|
||||
results.addResults.push({ success:result.success, error: result.error, objectId: objectId});
|
||||
if(result.success)
|
||||
{
|
||||
var phantomAdd = new Graphic(
|
||||
addEdit.geometry,
|
||||
self._getPhantomSymbol(addEdit.geometry, editsStore.ADD),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomAdd);
|
||||
}
|
||||
},this);
|
||||
adds = adds || [];
|
||||
adds.forEach(function(addEdit)
|
||||
{
|
||||
var objectId = this.getNextTempId();
|
||||
addEdit.attributes[ this.objectIdField ] = objectId;
|
||||
var result = editsStore.pushEdit(editsStore.ADD, this.url, addEdit);
|
||||
results.addResults.push({ success:result.success, error: result.error, objectId: objectId});
|
||||
if(result.success)
|
||||
{
|
||||
var phantomAdd = new Graphic(
|
||||
addEdit.geometry,
|
||||
self._getPhantomSymbol(addEdit.geometry, editsStore.ADD),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomAdd);
|
||||
}
|
||||
},this);
|
||||
|
||||
updates = updates || [];
|
||||
updates.forEach(function(updateEdit)
|
||||
{
|
||||
var objectId = updateEdit.attributes[ this.objectIdField ];
|
||||
var result = editsStore.pushEdit(editsStore.UPDATE, this.url, updateEdit);
|
||||
results.updateResults.push({success:result.success, error: result.error, objectId: objectId});
|
||||
updatesMap[ objectId ] = updateEdit;
|
||||
if(result.success)
|
||||
{
|
||||
var phantomUpdate = new Graphic(
|
||||
updateEdit.geometry,
|
||||
self._getPhantomSymbol(updateEdit.geometry, editsStore.UPDATE),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomUpdate);
|
||||
}
|
||||
},this);
|
||||
updates = updates || [];
|
||||
updates.forEach(function(updateEdit)
|
||||
{
|
||||
var objectId = updateEdit.attributes[ this.objectIdField ];
|
||||
var result = editsStore.pushEdit(editsStore.UPDATE, this.url, updateEdit);
|
||||
results.updateResults.push({success:result.success, error: result.error, objectId: objectId});
|
||||
updatesMap[ objectId ] = updateEdit;
|
||||
if(result.success)
|
||||
{
|
||||
var phantomUpdate = new Graphic(
|
||||
updateEdit.geometry,
|
||||
self._getPhantomSymbol(updateEdit.geometry, editsStore.UPDATE),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomUpdate);
|
||||
}
|
||||
},this);
|
||||
|
||||
deletes = deletes || [];
|
||||
deletes.forEach(function(deleteEdit)
|
||||
{
|
||||
var objectId = deleteEdit.attributes[ this.objectIdField ];
|
||||
var result = editsStore.pushEdit(editsStore.DELETE, this.url, deleteEdit);
|
||||
results.deleteResults.push({success:result.success, error: result.error, objectId: objectId});
|
||||
if(result.success)
|
||||
{
|
||||
var phantomDelete = new Graphic(
|
||||
deleteEdit.geometry,
|
||||
self._getPhantomSymbol(deleteEdit.geometry, editsStore.DELETE),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomDelete);
|
||||
}
|
||||
},this);
|
||||
deletes = deletes || [];
|
||||
deletes.forEach(function(deleteEdit)
|
||||
{
|
||||
var objectId = deleteEdit.attributes[ this.objectIdField ];
|
||||
var result = editsStore.pushEdit(editsStore.DELETE, this.url, deleteEdit);
|
||||
results.deleteResults.push({success:result.success, error: result.error, objectId: objectId});
|
||||
if(result.success)
|
||||
{
|
||||
var phantomDelete = new Graphic(
|
||||
deleteEdit.geometry,
|
||||
self._getPhantomSymbol(deleteEdit.geometry, editsStore.DELETE),
|
||||
{
|
||||
objectId: objectId
|
||||
});
|
||||
this._phantomLayer.add(phantomDelete);
|
||||
}
|
||||
},this);
|
||||
|
||||
/* we already pushed the edits into the local store, now we let the FeatureLayer to do the local updating of the layer graphics */
|
||||
setTimeout(function()
|
||||
{
|
||||
this._editHandler(results, adds, updatesMap, callback, errback, deferred);
|
||||
self.emit(self.events.EDITS_ENQUEUED, results);
|
||||
}.bind(this),0);
|
||||
return deferred;
|
||||
}
|
||||
}; // layer.applyEdits()
|
||||
|
||||
// we need to identify ADDs before sending them to the server
|
||||
// we assign temporary ids (using negative numbers to distinguish them from real ids)
|
||||
layer._nextTempId = -1;
|
||||
layer.getNextTempId = function()
|
||||
{
|
||||
return this._nextTempId--;
|
||||
};
|
||||
/* we already pushed the edits into the local store, now we let the FeatureLayer to do the local updating of the layer graphics */
|
||||
setTimeout(function()
|
||||
{
|
||||
this._editHandler(results, adds, updatesMap, callback, errback, deferred);
|
||||
self.emit(self.events.EDITS_ENQUEUED, results);
|
||||
}.bind(this),0);
|
||||
return deferred;
|
||||
}
|
||||
}; // layer.applyEdits()
|
||||
|
||||
function _initPhantomLayer()
|
||||
{
|
||||
layer._phantomLayer = new GraphicsLayer({opacity:0.8});
|
||||
layer._phantomLayer.disableMouseEvents();
|
||||
var index = layer.getMap().graphicsLayerIds.indexOf(layer.id);
|
||||
layer._map.addLayer(layer._phantomLayer,index-1);
|
||||
}
|
||||
_initPhantomLayer();
|
||||
// we need to identify ADDs before sending them to the server
|
||||
// we assign temporary ids (using negative numbers to distinguish them from real ids)
|
||||
layer._nextTempId = -1;
|
||||
layer.getNextTempId = function()
|
||||
{
|
||||
return this._nextTempId--;
|
||||
};
|
||||
|
||||
}, // extend
|
||||
function _initPhantomLayer()
|
||||
{
|
||||
layer._phantomLayer = new GraphicsLayer({opacity:0.8});
|
||||
layer._phantomLayer.disableMouseEvents();
|
||||
var index = layer.getMap().graphicsLayerIds.indexOf(layer.id);
|
||||
layer._map.addLayer(layer._phantomLayer,index-1);
|
||||
}
|
||||
_initPhantomLayer();
|
||||
|
||||
_phantomSymbols: [],
|
||||
}, // extend
|
||||
|
||||
_getPhantomSymbol: function(geometry, operation)
|
||||
{
|
||||
if( this._phantomSymbols.length == 0)
|
||||
{
|
||||
var color = [0,255,0,255];
|
||||
var width = 4;
|
||||
_phantomSymbols: [],
|
||||
|
||||
this._phantomSymbols['point'] = [];
|
||||
this._phantomSymbols['point'][editsStore.ADD] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSCross",
|
||||
"xoffset": 10, "yoffset": 10,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['point'][editsStore.UPDATE] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSCircle",
|
||||
"xoffset": 0, "yoffset": 0,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['point'][editsStore.DELETE] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSX",
|
||||
"xoffset": 0, "yoffset": 0,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['multipoint'] = null;
|
||||
_getPhantomSymbol: function(geometry, operation)
|
||||
{
|
||||
if( this._phantomSymbols.length == 0)
|
||||
{
|
||||
var color = [0,255,0,255];
|
||||
var width = 4;
|
||||
|
||||
this._phantomSymbols['polyline'] = [];
|
||||
this._phantomSymbols['polyline'][editsStore.ADD] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSSolid",
|
||||
"color": color,"width": width
|
||||
});
|
||||
this._phantomSymbols['polyline'][editsStore.UPDATE] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSDash",
|
||||
"color": color,"width": width
|
||||
});
|
||||
this._phantomSymbols['polyline'][editsStore.DELETE] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSDot",
|
||||
"color": color,"width": width
|
||||
});
|
||||
this._phantomSymbols['point'] = [];
|
||||
this._phantomSymbols['point'][editsStore.ADD] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSCross",
|
||||
"xoffset": 10, "yoffset": 10,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['point'][editsStore.UPDATE] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSCircle",
|
||||
"xoffset": 0, "yoffset": 0,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['point'][editsStore.DELETE] = new SimpleMarkerSymbol({
|
||||
"type": "esriSMS", "style": "esriSMSX",
|
||||
"xoffset": 0, "yoffset": 0,
|
||||
"color": [255,255,255,0], "size": 15,
|
||||
"outline": { "color": color, "width": width, "type": "esriSLS", "style": "esriSLSSolid" }
|
||||
});
|
||||
this._phantomSymbols['multipoint'] = null;
|
||||
|
||||
this._phantomSymbols['polygon'] = [];
|
||||
this._phantomSymbols['polygon'][editsStore.ADD] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSSolid", "color": color, "width": width }
|
||||
});
|
||||
this._phantomSymbols['polygon'][editsStore.UPDATE] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSDash", "color": color, "width": width }
|
||||
});
|
||||
this._phantomSymbols['polygon'][editsStore.DELETE] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSDot", "color": color, "width": width }
|
||||
});
|
||||
}
|
||||
this._phantomSymbols['polyline'] = [];
|
||||
this._phantomSymbols['polyline'][editsStore.ADD] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSSolid",
|
||||
"color": color,"width": width
|
||||
});
|
||||
this._phantomSymbols['polyline'][editsStore.UPDATE] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSDash",
|
||||
"color": color,"width": width
|
||||
});
|
||||
this._phantomSymbols['polyline'][editsStore.DELETE] = new SimpleLineSymbol({
|
||||
"type": "esriSLS", "style": "esriSLSDot",
|
||||
"color": color,"width": width
|
||||
});
|
||||
|
||||
return this._phantomSymbols[ geometry.type ][ operation ];
|
||||
},
|
||||
this._phantomSymbols['polygon'] = [];
|
||||
this._phantomSymbols['polygon'][editsStore.ADD] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSSolid", "color": color, "width": width }
|
||||
});
|
||||
this._phantomSymbols['polygon'][editsStore.UPDATE] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSDash", "color": color, "width": width }
|
||||
});
|
||||
this._phantomSymbols['polygon'][editsStore.DELETE] = new SimpleFillSymbol({
|
||||
"type": "esriSFS",
|
||||
"style": "esriSFSSolid",
|
||||
"color": [255,255,255,0],
|
||||
"outline": { "type": "esriSLS", "style": "esriSLSDot", "color": color, "width": width }
|
||||
});
|
||||
}
|
||||
|
||||
return this._phantomSymbols[ geometry.type ][ operation ];
|
||||
},
|
||||
|
||||
/**
|
||||
* Forces library into an offline state. Any edits applied during this condition will be stored locally
|
||||
*/
|
||||
goOffline: function()
|
||||
{
|
||||
console.log('going offline');
|
||||
this._onlineStatus = this.OFFLINE;
|
||||
},
|
||||
goOffline: function()
|
||||
{
|
||||
console.log('going offline');
|
||||
this._onlineStatus = this.OFFLINE;
|
||||
},
|
||||
|
||||
/**
|
||||
* Forces library to return to an online state. If there are pending edits,
|
||||
* an attempt will be made to sync them with the remote feature server
|
||||
* @param callback callback( boolean, errors )
|
||||
*/
|
||||
goOnline: function(callback)
|
||||
{
|
||||
console.log('going online');
|
||||
this._onlineStatus = this.RECONNECTING;
|
||||
this._replayStoredEdits(function()
|
||||
{
|
||||
this._onlineStatus = this.ONLINE;
|
||||
callback && callback.apply(this,arguments);
|
||||
}.bind(this));
|
||||
//this.refresh();
|
||||
},
|
||||
goOnline: function(callback)
|
||||
{
|
||||
console.log('going online');
|
||||
this._onlineStatus = this.RECONNECTING;
|
||||
this._replayStoredEdits(function()
|
||||
{
|
||||
this._onlineStatus = this.ONLINE;
|
||||
callback && callback.apply(this,arguments);
|
||||
}.bind(this));
|
||||
//this.refresh();
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if offline or online condition exists
|
||||
* @returns {string} ONLINE or OFFLINE
|
||||
*/
|
||||
getOnlineStatus: function()
|
||||
{
|
||||
return this._onlineStatus;
|
||||
},
|
||||
getOnlineStatus: function()
|
||||
{
|
||||
return this._onlineStatus;
|
||||
},
|
||||
|
||||
/**
|
||||
* A string value representing human readable information on pending edits
|
||||
* @param edit
|
||||
* @returns {string}
|
||||
*/
|
||||
getReadableEdit: function(edit)
|
||||
{
|
||||
var layer = this._featureLayers[ edit.layer ];
|
||||
var graphic = editsStore._deserialize(edit.graphic);
|
||||
var readableGraphic = graphic.geometry.type;
|
||||
var layerId = edit.layer.substring(edit.layer.lastIndexOf('/')+1);
|
||||
if(layer)
|
||||
readableGraphic += " [id=" + graphic.attributes[layer.objectIdField] + "]";
|
||||
return "o:" + edit.operation + ", l:" + layerId + ", g:" + readableGraphic;
|
||||
},
|
||||
getReadableEdit: function(edit)
|
||||
{
|
||||
var layer = this._featureLayers[ edit.layer ];
|
||||
var graphic = editsStore._deserialize(edit.graphic);
|
||||
var readableGraphic = graphic.geometry.type;
|
||||
var layerId = edit.layer.substring(edit.layer.lastIndexOf('/')+1);
|
||||
if(layer)
|
||||
readableGraphic += " [id=" + graphic.attributes[layer.objectIdField] + "]";
|
||||
return "o:" + edit.operation + ", l:" + layerId + ", g:" + readableGraphic;
|
||||
},
|
||||
|
||||
/* internal methods */
|
||||
/* internal methods */
|
||||
|
||||
_optimizeEditsQueue: function()
|
||||
{
|
||||
// console.log("edits:", editsStore._retrieveEditsQueue().map(function(e){return this.getReadableEdit(e)},this));
|
||||
var optimizedEdits = {};
|
||||
var editCount = editsStore.pendingEditsCount();
|
||||
var optimizedCount = 0;
|
||||
_optimizeEditsQueue: function()
|
||||
{
|
||||
// console.log("edits:", editsStore._retrieveEditsQueue().map(function(e){return this.getReadableEdit(e)},this));
|
||||
var optimizedEdits = {};
|
||||
var editCount = editsStore.pendingEditsCount();
|
||||
var optimizedCount = 0;
|
||||
|
||||
while( editsStore.hasPendingEdits() )
|
||||
{
|
||||
var edit = editsStore.popFirstEdit();
|
||||
var layer = this._featureLayers[ edit.layer ];
|
||||
while( editsStore.hasPendingEdits() )
|
||||
{
|
||||
var edit = editsStore.popFirstEdit();
|
||||
var layer = this._featureLayers[ edit.layer ];
|
||||
|
||||
if( ! (edit.layer in optimizedEdits) )
|
||||
optimizedEdits[edit.layer] = {}
|
||||
if( ! (edit.layer in optimizedEdits) )
|
||||
optimizedEdits[edit.layer] = {}
|
||||
|
||||
var layerEdits = optimizedEdits[edit.layer];
|
||||
var objectId = edit.graphic.attributes[ layer.objectIdField ];
|
||||
var layerEdits = optimizedEdits[edit.layer];
|
||||
var objectId = edit.graphic.attributes[ layer.objectIdField ];
|
||||
|
||||
if( !( objectId in layerEdits) )
|
||||
{
|
||||
// first edit we see of this feature, no optimization to apply
|
||||
layerEdits[ objectId ] = edit;
|
||||
optimizedCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we already have seen one edit for this same feature... we can merge the two edits in a single operation
|
||||
switch( edit.operation )
|
||||
{
|
||||
case editsStore.ADD:
|
||||
/* impossible!! */
|
||||
throw("can't add the same feature twice!");
|
||||
break;
|
||||
case editsStore.UPDATE:
|
||||
layerEdits[ objectId ].graphic = edit.graphic;
|
||||
break;
|
||||
case editsStore.DELETE:
|
||||
if(objectId < 0)
|
||||
{
|
||||
delete layerEdits[ objectId ];
|
||||
optimizedCount -= 1;
|
||||
}
|
||||
else
|
||||
layerEdits[objectId].operation = editsStore.DELETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( Object.keys(layerEdits).length == 0 )
|
||||
{
|
||||
delete optimizedEdits[edit.layer]
|
||||
}
|
||||
}
|
||||
if( !( objectId in layerEdits) )
|
||||
{
|
||||
// first edit we see of this feature, no optimization to apply
|
||||
layerEdits[ objectId ] = edit;
|
||||
optimizedCount += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// we already have seen one edit for this same feature... we can merge the two edits in a single operation
|
||||
switch( edit.operation )
|
||||
{
|
||||
case editsStore.ADD:
|
||||
/* impossible!! */
|
||||
throw("can't add the same feature twice!");
|
||||
break;
|
||||
case editsStore.UPDATE:
|
||||
layerEdits[ objectId ].graphic = edit.graphic;
|
||||
break;
|
||||
case editsStore.DELETE:
|
||||
if(objectId < 0)
|
||||
{
|
||||
delete layerEdits[ objectId ];
|
||||
optimizedCount -= 1;
|
||||
}
|
||||
else
|
||||
layerEdits[objectId].operation = editsStore.DELETE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( Object.keys(layerEdits).length == 0 )
|
||||
{
|
||||
delete optimizedEdits[edit.layer]
|
||||
}
|
||||
}
|
||||
|
||||
// console.log("optimized:",optimizedEdits);
|
||||
console.log("optimized", editCount, "edits into", optimizedCount,"edits of", Object.keys(optimizedEdits).length ,"layers");
|
||||
return optimizedEdits;
|
||||
},
|
||||
// console.log("optimized:",optimizedEdits);
|
||||
console.log("optimized", editCount, "edits into", optimizedCount,"edits of", Object.keys(optimizedEdits).length ,"layers");
|
||||
return optimizedEdits;
|
||||
},
|
||||
|
||||
_replayStoredEdits: function(callback)
|
||||
{
|
||||
if( editsStore.hasPendingEdits() )
|
||||
{
|
||||
//
|
||||
// flatten the queue into unique edits for each feature, grouped by FeatureLayer
|
||||
//
|
||||
var optimizedEdits = this._optimizeEditsQueue();
|
||||
var promises = {};
|
||||
_replayStoredEdits: function(callback)
|
||||
{
|
||||
if( editsStore.hasPendingEdits() )
|
||||
{
|
||||
//
|
||||
// flatten the queue into unique edits for each feature, grouped by FeatureLayer
|
||||
//
|
||||
var optimizedEdits = this._optimizeEditsQueue();
|
||||
var promises = {};
|
||||
|
||||
if( Object.keys(optimizedEdits).length == 0 )
|
||||
{
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true, {});
|
||||
return;
|
||||
}
|
||||
if( Object.keys(optimizedEdits).length == 0 )
|
||||
{
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true, {});
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// send edits for each of the layers
|
||||
//
|
||||
for(var layerUrl in optimizedEdits)
|
||||
{
|
||||
if(!optimizedEdits.hasOwnProperty(layerUrl))
|
||||
continue;
|
||||
//
|
||||
// send edits for each of the layers
|
||||
//
|
||||
for(var layerUrl in optimizedEdits)
|
||||
{
|
||||
if(!optimizedEdits.hasOwnProperty(layerUrl))
|
||||
continue;
|
||||
|
||||
var layer = this._featureLayers[ layerUrl ];
|
||||
var layerEdits = optimizedEdits[layerUrl];
|
||||
var layer = this._featureLayers[ layerUrl ];
|
||||
var layerEdits = optimizedEdits[layerUrl];
|
||||
|
||||
console.assert(Object.keys(layerEdits).length != 0)
|
||||
console.assert(Object.keys(layerEdits).length != 0)
|
||||
|
||||
layer.__onEditsComplete = layer["onEditsComplete"];
|
||||
layer["onEditsComplete"] = function() { console.log("intercepting events onEditsComplete");}
|
||||
layer.__onBeforeApplyEdits = layer["onBeforeApplyEdits"];
|
||||
layer["onBeforeApplyEdits"] = function() { console.log("intercepting events onBeforeApplyEdits");}
|
||||
layer.__onEditsComplete = layer["onEditsComplete"];
|
||||
layer["onEditsComplete"] = function() { console.log("intercepting events onEditsComplete");}
|
||||
layer.__onBeforeApplyEdits = layer["onBeforeApplyEdits"];
|
||||
layer["onBeforeApplyEdits"] = function() { console.log("intercepting events onBeforeApplyEdits");}
|
||||
|
||||
var adds = [], updates = [], deletes = [];
|
||||
for(var objectId in layerEdits)
|
||||
{
|
||||
if(!layerEdits.hasOwnProperty(objectId))
|
||||
continue;
|
||||
var adds = [], updates = [], deletes = [];
|
||||
for(var objectId in layerEdits)
|
||||
{
|
||||
if(!layerEdits.hasOwnProperty(objectId))
|
||||
continue;
|
||||
|
||||
var edit = layerEdits[objectId];
|
||||
switch(edit.operation)
|
||||
{
|
||||
case editsStore.ADD:
|
||||
for(var i=0; i<layer.graphics.length; i++)
|
||||
{
|
||||
var g = layer.graphics[i];
|
||||
if( g.attributes[layer.objectIdField] == edit.graphic.attributes[layer.objectIdField] )
|
||||
{
|
||||
layer.remove(g);
|
||||
break;
|
||||
}
|
||||
};
|
||||
delete edit.graphic.attributes[ layer.objectIdField ];
|
||||
adds.push(edit.graphic);
|
||||
break;
|
||||
case editsStore.UPDATE:
|
||||
updates.push(edit.graphic);
|
||||
break;
|
||||
case editsStore.DELETE:
|
||||
deletes.push(edit.graphic)
|
||||
break;
|
||||
}
|
||||
}
|
||||
var edit = layerEdits[objectId];
|
||||
switch(edit.operation)
|
||||
{
|
||||
case editsStore.ADD:
|
||||
for(var i=0; i<layer.graphics.length; i++)
|
||||
{
|
||||
var g = layer.graphics[i];
|
||||
if( g.attributes[layer.objectIdField] == edit.graphic.attributes[layer.objectIdField] )
|
||||
{
|
||||
layer.remove(g);
|
||||
break;
|
||||
}
|
||||
};
|
||||
delete edit.graphic.attributes[ layer.objectIdField ];
|
||||
adds.push(edit.graphic);
|
||||
break;
|
||||
case editsStore.UPDATE:
|
||||
updates.push(edit.graphic);
|
||||
break;
|
||||
case editsStore.DELETE:
|
||||
deletes.push(edit.graphic)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
promises[layerUrl] = function(layer)
|
||||
{
|
||||
// unfortunately we can't use the promise that is returned from layer._applyEdits()
|
||||
// because it returns 3 result parameters (addResults,updateResults,deleteResults)
|
||||
// and when we combine all promises in the dojo/promise/all() method below this only
|
||||
// supports promises that return one value
|
||||
var dfd = new Deferred();
|
||||
layer._applyEdits(adds,updates,deletes,
|
||||
function(addResults,updateResults,deleteResults)
|
||||
{
|
||||
layer._phantomLayer.clear();
|
||||
layer["onEditsComplete"] = layer.__onEditsComplete; delete layer.__onEditsComplete;
|
||||
layer["onBeforeApplyEdits"] = layer.__onBeforeApplyEdits; delete layer.__onBeforeApplyEdits;
|
||||
dfd.resolve({addResults:addResults,updateResults:updateResults,deleteResults:deleteResults}); // wrap three arguments in a single object
|
||||
},
|
||||
function(error)
|
||||
{
|
||||
layer["onEditsComplete"] = layer.__onEditsComplete; delete layer.__onEditsComplete;
|
||||
layer["onBeforeApplyEdits"] = layer.__onBeforeApplyEdits; delete layer.__onBeforeApplyEdits;
|
||||
dfd.reject(error);
|
||||
}
|
||||
);
|
||||
return dfd;
|
||||
}(layer);
|
||||
}
|
||||
promises[layerUrl] = function(layer)
|
||||
{
|
||||
// unfortunately we can't use the promise that is returned from layer._applyEdits()
|
||||
// because it returns 3 result parameters (addResults,updateResults,deleteResults)
|
||||
// and when we combine all promises in the dojo/promise/all() method below this only
|
||||
// supports promises that return one value
|
||||
var dfd = new Deferred();
|
||||
layer._applyEdits(adds,updates,deletes,
|
||||
function(addResults,updateResults,deleteResults)
|
||||
{
|
||||
layer._phantomLayer.clear();
|
||||
layer["onEditsComplete"] = layer.__onEditsComplete; delete layer.__onEditsComplete;
|
||||
layer["onBeforeApplyEdits"] = layer.__onBeforeApplyEdits; delete layer.__onBeforeApplyEdits;
|
||||
dfd.resolve({addResults:addResults,updateResults:updateResults,deleteResults:deleteResults}); // wrap three arguments in a single object
|
||||
},
|
||||
function(error)
|
||||
{
|
||||
layer["onEditsComplete"] = layer.__onEditsComplete; delete layer.__onEditsComplete;
|
||||
layer["onBeforeApplyEdits"] = layer.__onBeforeApplyEdits; delete layer.__onBeforeApplyEdits;
|
||||
dfd.reject(error);
|
||||
}
|
||||
);
|
||||
return dfd;
|
||||
}(layer);
|
||||
}
|
||||
|
||||
//
|
||||
// wait for all requests to finish
|
||||
//
|
||||
var allPromises = new all(promises);
|
||||
allPromises.then(
|
||||
function(responses)
|
||||
{
|
||||
console.log("all responses are back");
|
||||
this.emit(this.events.EDITS_SENT);
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true,responses);
|
||||
}.bind(this),
|
||||
function(errors)
|
||||
{
|
||||
console.log("ERROR!!");
|
||||
console.log(errors);
|
||||
callback && callback(false,errors);
|
||||
}.bind(this));
|
||||
} // hasPendingEdits()
|
||||
else
|
||||
{
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true, {});
|
||||
}
|
||||
},
|
||||
//
|
||||
// wait for all requests to finish
|
||||
//
|
||||
var allPromises = new all(promises);
|
||||
allPromises.then(
|
||||
function(responses)
|
||||
{
|
||||
console.log("all responses are back");
|
||||
this.emit(this.events.EDITS_SENT);
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true,responses);
|
||||
}.bind(this),
|
||||
function(errors)
|
||||
{
|
||||
console.log("ERROR!!");
|
||||
console.log(errors);
|
||||
callback && callback(false,errors);
|
||||
}.bind(this));
|
||||
} // hasPendingEdits()
|
||||
else
|
||||
{
|
||||
this.emit(this.events.ALL_EDITS_SENT);
|
||||
callback && callback(true, {});
|
||||
}
|
||||
},
|
||||
|
||||
}); // declare
|
||||
}); // declare
|
||||
}); // define
|
||||
|
||||
@ -1,26 +1,26 @@
|
||||
"use strict"
|
||||
|
||||
define([
|
||||
"dojo/query",
|
||||
"dojo/query",
|
||||
"dojo/request",
|
||||
"esri/geometry",
|
||||
"tiles/base64utils",
|
||||
"tiles/dbStore",
|
||||
"tiles/tilingScheme",
|
||||
"tiles/FileSaver"
|
||||
], function(query, request, geometry,Base64Utils,DbStore,TilingScheme,FileSaver)
|
||||
{
|
||||
return {
|
||||
"esri/geometry",
|
||||
"tiles/base64utils",
|
||||
"tiles/dbStore",
|
||||
"tiles/tilingScheme",
|
||||
"tiles/FileSaver"
|
||||
], function(query, request, geometry,Base64Utils,DbStore,TilingScheme,FileSaver)
|
||||
{
|
||||
return {
|
||||
/**
|
||||
* Utility method to get the basemap layer reference
|
||||
* @param map
|
||||
* @returns {Number} layerId
|
||||
*/
|
||||
getBasemapLayer: function(map)
|
||||
{
|
||||
var layerId = map.layerIds[0];
|
||||
return map.getLayer(layerId);
|
||||
},
|
||||
getBasemapLayer: function(map)
|
||||
{
|
||||
var layerId = map.layerIds[0];
|
||||
return map.getLayer(layerId);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method that extends a layer object with the offline capability.
|
||||
@ -29,27 +29,27 @@ define([
|
||||
* @param callback
|
||||
* @returns {callback} callback(boolean, string)
|
||||
*/
|
||||
extend: function(layer,callback)
|
||||
{
|
||||
extend: function(layer,callback)
|
||||
{
|
||||
console.log("extending layer", layer.url);
|
||||
|
||||
layer._lastTileUrl = "";
|
||||
|
||||
/* we add some methods to the layer object */
|
||||
/* we don't want to extend the tiled layer class, as it is a capability that we want to add only to one instance */
|
||||
/* we also add some additional attributes inside an "offline" object */
|
||||
/* we don't want to extend the tiled layer class, as it is a capability that we want to add only to one instance */
|
||||
/* we also add some additional attributes inside an "offline" object */
|
||||
|
||||
layer._getTileUrl = layer.getTileUrl;
|
||||
layer.offline = {
|
||||
online: true,
|
||||
store: new DbStore(),
|
||||
proxyPath: "../../lib/proxy.php"
|
||||
};
|
||||
layer._getTileUrl = layer.getTileUrl;
|
||||
layer.offline = {
|
||||
online: true,
|
||||
store: new DbStore(),
|
||||
proxyPath: "../../lib/proxy.php"
|
||||
};
|
||||
|
||||
if( /*false &&*/ layer.offline.store.isSupported() )
|
||||
layer.offline.store.init(callback);
|
||||
else
|
||||
return callback(false, "indexedDB not supported");
|
||||
if( /*false &&*/ layer.offline.store.isSupported() )
|
||||
layer.offline.store.init(callback);
|
||||
else
|
||||
return callback(false, "indexedDB not supported");
|
||||
|
||||
/**
|
||||
* Internal method that overrides the getTileUrl() method.
|
||||
@ -62,52 +62,52 @@ define([
|
||||
* @param col
|
||||
* @returns {String} URL
|
||||
*/
|
||||
layer.getTileUrl = function(level,row,col)
|
||||
{
|
||||
console.log("looking for tile",level,row,col);
|
||||
var url = this._getTileUrl(level,row,col);
|
||||
layer.getTileUrl = function(level,row,col)
|
||||
{
|
||||
console.log("looking for tile",level,row,col);
|
||||
var url = this._getTileUrl(level,row,col);
|
||||
|
||||
if( this.offline.online )
|
||||
{
|
||||
if( this.offline.online )
|
||||
{
|
||||
console.log("fetching url online: ", url);
|
||||
layer._lastTileUrl = url;
|
||||
return url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
url = url.split('?')[0];
|
||||
url = url.split('?')[0];
|
||||
|
||||
/* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */
|
||||
var tileid = "void:"+level+"-"+row+"-"+col;
|
||||
/* temporary URL returned immediately, as we haven't retrieved the image from the indexeddb yet */
|
||||
var tileid = "void:"+level+"-"+row+"-"+col;
|
||||
|
||||
this.offline.store.get(url, function(success, offlineTile)
|
||||
{
|
||||
/* when the .get() callback is called we replace the temporary URL originally returned by the data:image url */
|
||||
var img = query("img[src="+tileid+"]")[0];
|
||||
this.offline.store.get(url, function(success, offlineTile)
|
||||
{
|
||||
/* when the .get() callback is called we replace the temporary URL originally returned by the data:image url */
|
||||
var img = query("img[src="+tileid+"]")[0];
|
||||
|
||||
if( success )
|
||||
{
|
||||
console.log("found tile offline", url);
|
||||
var imgURL = "data:image;base64," + offlineTile.img;
|
||||
if( success )
|
||||
{
|
||||
console.log("found tile offline", url);
|
||||
var imgURL = "data:image;base64," + offlineTile.img;
|
||||
|
||||
// search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url
|
||||
img.style.borderColor = "blue";
|
||||
// when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method
|
||||
// sets img visibility to 'hidden', so we need to show the image back once we have put the data:image
|
||||
img.style.visibility = "visible";
|
||||
img.src = imgURL;
|
||||
// search for the img with src="void:"+level+"-"+row+"-"+col and replace with actual url
|
||||
img.style.borderColor = "blue";
|
||||
// when we return a nonexistent url to the image, the TiledMapServiceLayer::_tileErrorHandler() method
|
||||
// sets img visibility to 'hidden', so we need to show the image back once we have put the data:image
|
||||
img.style.visibility = "visible";
|
||||
img.src = imgURL;
|
||||
|
||||
return ""; /* this result goes nowhere, seriously */
|
||||
}
|
||||
else
|
||||
{
|
||||
img.style.borderColor = "green";
|
||||
console.log("tile is not in the offline store", url);
|
||||
return ""; /* this result goes nowhere, seriously */
|
||||
}
|
||||
});
|
||||
return ""; /* this result goes nowhere, seriously */
|
||||
}
|
||||
else
|
||||
{
|
||||
img.style.borderColor = "green";
|
||||
console.log("tile is not in the offline store", url);
|
||||
return ""; /* this result goes nowhere, seriously */
|
||||
}
|
||||
});
|
||||
|
||||
return tileid;
|
||||
};
|
||||
return tileid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an object that contains the number of tiles that would need to be downloaded
|
||||
@ -140,32 +140,32 @@ define([
|
||||
* @param extent
|
||||
* @param reportProgress
|
||||
*/
|
||||
layer.prepareForOffline = function(minLevel, maxLevel, extent, reportProgress)
|
||||
{
|
||||
/* create list of tiles to store */
|
||||
var tilingScheme = new TilingScheme(this,geometry);
|
||||
var cells = [];
|
||||
layer.prepareForOffline = function(minLevel, maxLevel, extent, reportProgress)
|
||||
{
|
||||
/* create list of tiles to store */
|
||||
var tilingScheme = new TilingScheme(this,geometry);
|
||||
var cells = [];
|
||||
|
||||
for(var level=minLevel; level<=maxLevel; level++)
|
||||
{
|
||||
var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level);
|
||||
for(var level=minLevel; level<=maxLevel; level++)
|
||||
{
|
||||
var level_cell_ids = tilingScheme.getAllCellIdsInExtent(extent,level);
|
||||
|
||||
level_cell_ids.forEach(function(cell_id)
|
||||
{
|
||||
cells.push({ level: level, row: cell_id[1], col: cell_id[0]});
|
||||
});
|
||||
level_cell_ids.forEach(function(cell_id)
|
||||
{
|
||||
cells.push({ level: level, row: cell_id[1], col: cell_id[0]});
|
||||
});
|
||||
|
||||
// if the number of requested tiles is excessive, we just stop
|
||||
if( cells.length > 5000 && level != maxLevel)
|
||||
{
|
||||
console.log("enough is enough!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if the number of requested tiles is excessive, we just stop
|
||||
if( cells.length > 5000 && level != maxLevel)
|
||||
{
|
||||
console.log("enough is enough!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* launch tile download */
|
||||
this._doNextTile(0, cells, reportProgress);
|
||||
};
|
||||
/* launch tile download */
|
||||
this._doNextTile(0, cells, reportProgress);
|
||||
};
|
||||
|
||||
/**
|
||||
* This method puts the layer in offline mode. When in offline mode,
|
||||
@ -177,10 +177,10 @@ define([
|
||||
* Used in conjunction with the offline dectection library, you can put the layer in
|
||||
* the appropriate mode when the offline condition changes.
|
||||
*/
|
||||
layer.goOffline = function()
|
||||
{
|
||||
this.offline.online = false;
|
||||
};
|
||||
layer.goOffline = function()
|
||||
{
|
||||
this.offline.online = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* This method puts the layer in online mode. When in online mode, the layer will
|
||||
@ -188,171 +188,171 @@ define([
|
||||
* If there is no internet connectivity the tiles may appear thanks to the browsers cache,
|
||||
* but no attempt will be made to look up tiles in the local database.
|
||||
*/
|
||||
layer.goOnline = function()
|
||||
{
|
||||
this.offline.online = true;
|
||||
this.refresh();
|
||||
};
|
||||
layer.goOnline = function()
|
||||
{
|
||||
this.offline.online = true;
|
||||
this.refresh();
|
||||
};
|
||||
|
||||
/**
|
||||
* Determines if application is online or offline
|
||||
* @returns {boolean}
|
||||
*/
|
||||
layer.isOnline = function()
|
||||
{
|
||||
return this.offline.online;
|
||||
};
|
||||
layer.isOnline = function()
|
||||
{
|
||||
return this.offline.online;
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears the local cache of tiles.
|
||||
* @param callback callback(boolean, errors)
|
||||
*/
|
||||
layer.deleteAllTiles = function(callback) // callback(success) or callback(false, error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
store.deleteAll(callback);
|
||||
}
|
||||
layer.deleteAllTiles = function(callback) // callback(success) or callback(false, error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
store.deleteAll(callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size in bytes of the local tile cache.
|
||||
* @param callback callback(size, error)
|
||||
*/
|
||||
layer.getOfflineUsage = function(callback) // callback({size: <>, tileCount: <>}) or callback(null,error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
store.size(callback);
|
||||
};
|
||||
layer.getOfflineUsage = function(callback) // callback({size: <>, tileCount: <>}) or callback(null,error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
store.size(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets polygons representing all cached cell ids within a particular
|
||||
* zoom level and bounded by an extent.
|
||||
* @param callback callback(polygon, error)
|
||||
*/
|
||||
layer.getTilePolygons = function(callback) // callback(Polygon polygon) or callback(null, error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var tilingScheme = new TilingScheme(this,geometry);
|
||||
store.getAllTiles(function(url,img,err)
|
||||
{
|
||||
if(url)
|
||||
{
|
||||
var components = url.split("/");
|
||||
var level = parseInt(components[ components.length - 3]);
|
||||
var col = parseInt(components[ components.length - 2]);
|
||||
var row = parseInt(components[ components.length - 1]);
|
||||
var cellId = [row,col];
|
||||
var polygon = tilingScheme.getCellPolygonFromCellId(cellId, level);
|
||||
//if( level == 15)
|
||||
callback(polygon);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(null,err);
|
||||
}
|
||||
});
|
||||
}
|
||||
layer.getTilePolygons = function(callback) // callback(Polygon polygon) or callback(null, error)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var tilingScheme = new TilingScheme(this,geometry);
|
||||
store.getAllTiles(function(url,img,err)
|
||||
{
|
||||
if(url)
|
||||
{
|
||||
var components = url.split("/");
|
||||
var level = parseInt(components[ components.length - 3]);
|
||||
var col = parseInt(components[ components.length - 2]);
|
||||
var row = parseInt(components[ components.length - 1]);
|
||||
var cellId = [row,col];
|
||||
var polygon = tilingScheme.getCellPolygonFromCellId(cellId, level);
|
||||
//if( level == 15)
|
||||
callback(polygon);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(null,err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves tile cache into a portable csv format.
|
||||
* @param fileName
|
||||
* @param callback callback( boolean, error)
|
||||
*/
|
||||
layer.saveToFile = function(fileName, callback) // callback(success, msg)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var csv = [];
|
||||
layer.saveToFile = function(fileName, callback) // callback(success, msg)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var csv = [];
|
||||
|
||||
csv.push("url,img");
|
||||
store.getAllTiles(function(url,img,evt)
|
||||
{
|
||||
if(evt=="end")
|
||||
{
|
||||
var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"});
|
||||
var saver = FileSaver.saveAs(blob, fileName);
|
||||
|
||||
if( saver.readyState == saver.DONE )
|
||||
{
|
||||
if( saver.error )
|
||||
return callback(false,"Error saving file " + fileName);
|
||||
else
|
||||
return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
saver.onerror = function(evt) {
|
||||
callback(false,"Error saving file " + fileName);
|
||||
}
|
||||
saver.onwriteend = function(evt)
|
||||
{
|
||||
callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
csv.push(url+","+img);
|
||||
}
|
||||
})
|
||||
}
|
||||
csv.push("url,img");
|
||||
store.getAllTiles(function(url,img,evt)
|
||||
{
|
||||
if(evt=="end")
|
||||
{
|
||||
var blob = new Blob([ csv.join("\r\n") ], {type:"text/plain;charset=utf-8"});
|
||||
var saver = FileSaver.saveAs(blob, fileName);
|
||||
|
||||
if( saver.readyState == saver.DONE )
|
||||
{
|
||||
if( saver.error )
|
||||
return callback(false,"Error saving file " + fileName);
|
||||
else
|
||||
return callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
saver.onerror = function(evt) {
|
||||
callback(false,"Error saving file " + fileName);
|
||||
}
|
||||
saver.onwriteend = function(evt)
|
||||
{
|
||||
callback(true, "Saved " + (csv.length-1) + " tiles (" + Math.floor(blob.size / 1024 / 1024 * 100) / 100 + " Mb) into " + fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
csv.push(url+","+img);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a csv file into local tile cache.
|
||||
* @param file
|
||||
* @param callback callback( boolean, error)
|
||||
*/
|
||||
layer.loadFromFile = function(file, callback) // callback(success,msg)
|
||||
{
|
||||
console.log("reading",file);
|
||||
layer.loadFromFile = function(file, callback) // callback(success,msg)
|
||||
{
|
||||
console.log("reading",file);
|
||||
|
||||
var store = this.offline.store;
|
||||
var layer = this;
|
||||
var store = this.offline.store;
|
||||
var layer = this;
|
||||
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob)
|
||||
{
|
||||
// Great success! All the File APIs are supported.
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(evt)
|
||||
{
|
||||
var csvContent = evt.target.result;
|
||||
var tiles = csvContent.split("\r\n");
|
||||
var tileCount = 0;
|
||||
if (window.File && window.FileReader && window.FileList && window.Blob)
|
||||
{
|
||||
// Great success! All the File APIs are supported.
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(evt)
|
||||
{
|
||||
var csvContent = evt.target.result;
|
||||
var tiles = csvContent.split("\r\n");
|
||||
var tileCount = 0;
|
||||
|
||||
if(tiles[0] != "url,img")
|
||||
return callback(false, "File " + file.name + " doesn't contain tiles that can be loaded");
|
||||
if(tiles[0] != "url,img")
|
||||
return callback(false, "File " + file.name + " doesn't contain tiles that can be loaded");
|
||||
|
||||
for(var i=1; i<tiles.length; i++)
|
||||
{
|
||||
var pair = tiles[i].split(',');
|
||||
var tile = {
|
||||
url: pair[0],
|
||||
img: pair[1]
|
||||
}
|
||||
console.log("read",tile.url);
|
||||
store.add(tile,function(success)
|
||||
{
|
||||
console.log(".");
|
||||
for(var i=1; i<tiles.length; i++)
|
||||
{
|
||||
var pair = tiles[i].split(',');
|
||||
var tile = {
|
||||
url: pair[0],
|
||||
img: pair[1]
|
||||
}
|
||||
console.log("read",tile.url);
|
||||
store.add(tile,function(success)
|
||||
{
|
||||
console.log(".");
|
||||
|
||||
if( success )
|
||||
tileCount += 1;
|
||||
if( success )
|
||||
tileCount += 1;
|
||||
|
||||
if( tileCount == tiles.length-1)
|
||||
{
|
||||
console.log("finished!");
|
||||
window.setTimeout(function() { /* refresh layer by zooming in and out, or some way that really refreshes the layer */ }, 1000);
|
||||
callback(true, tileCount + " tiles loaded from " + file.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(false, 'The File APIs are not fully supported in this browser.');
|
||||
}
|
||||
}
|
||||
if( tileCount == tiles.length-1)
|
||||
{
|
||||
console.log("finished!");
|
||||
window.setTimeout(function() { /* refresh layer by zooming in and out, or some way that really refreshes the layer */ }, 1000);
|
||||
callback(true, tileCount + " tiles loaded from " + file.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
callback(false, 'The File APIs are not fully supported in this browser.');
|
||||
}
|
||||
}
|
||||
|
||||
/* internal methods */
|
||||
/* internal methods */
|
||||
|
||||
/**
|
||||
* Makes a request to a tile url and uses that as a basis for the
|
||||
@ -361,8 +361,8 @@ define([
|
||||
* @param callback
|
||||
* @returns {Number} Returns NaN if there was a problem retrieving the tile
|
||||
*/
|
||||
layer.estimateTileSize = function(callback)
|
||||
{
|
||||
layer.estimateTileSize = function(callback)
|
||||
{
|
||||
if(layer._lastTileUrl){
|
||||
var url = this.offline.proxyPath + "?" + layer._lastTileUrl;
|
||||
request.get(url,{
|
||||
@ -379,70 +379,70 @@ define([
|
||||
else{
|
||||
callback(NaN);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
layer._doNextTile = function(i, cells, reportProgress)
|
||||
{
|
||||
var cell = cells[i];
|
||||
var error;
|
||||
layer._doNextTile = function(i, cells, reportProgress)
|
||||
{
|
||||
var cell = cells[i];
|
||||
var error;
|
||||
|
||||
this._storeTile(cell.level,cell.row,cell.col, function(success, error)
|
||||
{
|
||||
if(!success)
|
||||
{
|
||||
console.log("error storing tile", cell, error);
|
||||
error = { cell:cell, msg:error};
|
||||
}
|
||||
this._storeTile(cell.level,cell.row,cell.col, function(success, error)
|
||||
{
|
||||
if(!success)
|
||||
{
|
||||
console.log("error storing tile", cell, error);
|
||||
error = { cell:cell, msg:error};
|
||||
}
|
||||
|
||||
var cancelRequested = reportProgress({countNow:i, countMax:cells.length, error: error, finishedDownloading:false});
|
||||
var cancelRequested = reportProgress({countNow:i, countMax:cells.length, error: error, finishedDownloading:false});
|
||||
|
||||
if( cancelRequested || i==cells.length-1 )
|
||||
reportProgress({ finishedDownloading: true, cancelRequested: cancelRequested})
|
||||
else
|
||||
this._doNextTile(i+1, cells, reportProgress);
|
||||
|
||||
}.bind(this))
|
||||
}
|
||||
if( cancelRequested || i==cells.length-1 )
|
||||
reportProgress({ finishedDownloading: true, cancelRequested: cancelRequested})
|
||||
else
|
||||
this._doNextTile(i+1, cells, reportProgress);
|
||||
|
||||
layer._storeTile = function(level,row,col,callback) // callback(success, msg)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var url = this._getTileUrl(level,row,col);
|
||||
url = url.split('?')[0];
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
/* download the tile */
|
||||
var imgurl = this.offline.proxyPath + "?" + url;
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", imgurl, true);
|
||||
req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data
|
||||
layer._storeTile = function(level,row,col,callback) // callback(success, msg)
|
||||
{
|
||||
var store = this.offline.store;
|
||||
var url = this._getTileUrl(level,row,col);
|
||||
url = url.split('?')[0];
|
||||
|
||||
req.onload = function()
|
||||
{
|
||||
if( req.status == 200 && req.responseText != "")
|
||||
{
|
||||
var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText));
|
||||
/* download the tile */
|
||||
var imgurl = this.offline.proxyPath + "?" + url;
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", imgurl, true);
|
||||
req.overrideMimeType("text/plain; charset=x-user-defined"); // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest?redirectlocale=en-US&redirectslug=DOM%2FXMLHttpRequest%2FUsing_XMLHttpRequest#Handling_binary_data
|
||||
|
||||
var tile = {
|
||||
url: url,
|
||||
img: img
|
||||
}
|
||||
req.onload = function()
|
||||
{
|
||||
if( req.status == 200 && req.responseText != "")
|
||||
{
|
||||
var img = Base64Utils.wordToBase64(Base64Utils.stringToWord(this.responseText));
|
||||
|
||||
store.add(tile,callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("xhr failed for", imgurl);
|
||||
callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl);
|
||||
}
|
||||
}
|
||||
req.onerror = function(e)
|
||||
{
|
||||
console.log("xhr failed for", imgurl);
|
||||
callback(false, e);
|
||||
}
|
||||
req.send(null);
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
var tile = {
|
||||
url: url,
|
||||
img: img
|
||||
}
|
||||
|
||||
store.add(tile,callback);
|
||||
}
|
||||
else
|
||||
{
|
||||
console.log("xhr failed for", imgurl);
|
||||
callback(false, req.status + " " + req.statusText + ": " + req.response + " when downloading " + imgurl);
|
||||
}
|
||||
}
|
||||
req.onerror = function(e)
|
||||
{
|
||||
console.log("xhr failed for", imgurl);
|
||||
callback(false, e);
|
||||
}
|
||||
req.send(null);
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user