WebGLImage.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  1. /* COPYRIGHT 2012 SUPERMAP
  2. * 本程序只能在有效的授权许可下使用。
  3. * 未经许可,不得以任何手段擅自使用或传播。*/
  4. /**
  5. * @requires SuperMap/Tile.js
  6. */
  7. /**
  8. * Class: SuperMap.Tile.WebGLImage
  9. * 瓦片拼接类(webgl)。
  10. * 用于管理图层图像拼贴。
  11. *
  12. * Inherits from:
  13. * - <SuperMap.Tile>
  14. */
  15. SuperMap.Tile.WebGLImage = SuperMap.Class(SuperMap.Tile, {
  16. /**
  17. * Property: url
  18. * {String} The URL of the image being requested. No default. Filled in by
  19. * layer.getURL() function.
  20. * 图片的url。
  21. */
  22. url: null,
  23. /**
  24. * Property: newImgTag
  25. * {String} tile最近请求的image的标签。
  26. */
  27. newImgTag:null,
  28. /**
  29. * Property: canvasType
  30. */
  31. canvasType: null,
  32. /**
  33. * Property: frame
  34. * {DOMElement} The canvas element is appended to the frame. Any gutter on
  35. * the canvas will be hidden behind the frame.
  36. */
  37. frame: null,
  38. /**
  39. * Property: isLoading
  40. * {Boolean} Indicates if the tile is currently waiting on a loading image.
  41. */
  42. isLoading: false,
  43. /**
  44. * Property: canvas
  45. * {DOMElement} The canvas element on which the image is drawn.
  46. */
  47. canvas: null,
  48. /**
  49. * Property: lastImage
  50. * {Image} The last requested image object. This property is used to make sure
  51. * that only the recent image is drawn.
  52. */
  53. lastImage: null,
  54. /**
  55. * Property: lastBounds
  56. * {<SuperMap.Bounds>} The bounds of the last requested image, needed for
  57. * VirtualCanvasImage.displayImage().
  58. */
  59. lastBounds: null,
  60. /**
  61. * Property: isBackBuffer
  62. * {Boolean} Is this tile a back buffer tile?
  63. */
  64. isBackBuffer: false,
  65. /**
  66. * Property: backBufferTile
  67. * {<SuperMap.Tile>} A clone of the tile used to create transition
  68. * effects when the tile is moved or changes resolution.
  69. */
  70. backBufferTile: null,
  71. /**
  72. * Property: fadingTimer
  73. * {Number} 记录fading动画的索引ID
  74. *
  75. */
  76. fadingTimer:null,
  77. /**
  78. * Constructor: SuperMap.Tile.WebGLImage
  79. * 瓦片拼接类。
  80. *
  81. * Parameters:
  82. * layer - {<SuperMap.Layer>} 瓦片所在的图层。
  83. * position - {<SuperMap.Pixel>}左上角的点。
  84. * bounds - {<SuperMap.Bounds>}瓦片大小。
  85. * url - {<String>}瓦片对应的url地址。
  86. * size - {<SuperMap.Size>}瓦片的size。
  87. * canvasType -
  88. */
  89. initialize: function(layer, position, bounds, url, size, canvasType) {
  90. var me = this;
  91. SuperMap.Tile.prototype.initialize.apply(me, arguments);
  92. me.url = url; //deprecated remove me
  93. me.canvasType = canvasType;
  94. me.events.addEventType("reprojectionProgress");
  95. me.events.addEventType("filterProgress");
  96. },
  97. /**
  98. * APIMethod: destroy
  99. * 解构 WebGLImage 类,释放资源。
  100. */
  101. destroy: function() {
  102. SuperMap.Tile.prototype.destroy.apply(this, arguments);
  103. var me = this;
  104. me.lastImage = null;
  105. me.canvas = null;
  106. me.canvasContext = null;
  107. // clean up the backBufferTile if it exists
  108. if (me.backBufferTile) {
  109. me.backBufferTile.destroy();
  110. me.backBufferTile = null;
  111. me.layer.events.unregister("loadend", me, me.hideBackBuffer);
  112. }
  113. },
  114. /**
  115. * Method: clone
  116. * 创建当前类的副本。
  117. * Parameters:
  118. * obj - {<SuperMap.Tile.Image>} The tile to be cloned
  119. *
  120. * Returns:
  121. * {<SuperMap.Tile.Image>} An exact clone of this <SuperMap.Tile.WebGLImage>
  122. */
  123. clone: function (obj) {
  124. var me = this;
  125. if (obj == null) {
  126. obj = new SuperMap.Tile.WebGLImage(me.layer,
  127. me.position,
  128. me.bounds,
  129. me.url,
  130. me.size,
  131. me.canvasType);
  132. }
  133. //pick up properties from superclass
  134. obj = SuperMap.Tile.prototype.clone.apply(me, [obj]);
  135. // a new canvas element should be created for the clone
  136. obj.canvas = null;
  137. return obj;
  138. },
  139. /**
  140. * Method: draw
  141. * Check that a tile should be drawn, and draw it. Starts a
  142. * transition if the layer requests one.
  143. *
  144. * Returns:
  145. * {Boolean} Always returns true.
  146. */
  147. draw: function() {
  148. var me = this;
  149. if (me.layer != me.layer.map.baseLayer && me.layer.reproject) {
  150. me.bounds = me.getBoundsFromBaseLayer(me.position);
  151. }
  152. var drawTile = SuperMap.Tile.prototype.draw.apply(me, arguments);
  153. me.startTransition(drawTile);
  154. if (!drawTile) {
  155. return;
  156. }
  157. if (me.isLoading) {
  158. // if we're already loading, send 'reload' instead of 'loadstart'.
  159. me.events.triggerEvent("reload");
  160. } else {
  161. me.isLoading = true;
  162. me.events.triggerEvent("loadstart");
  163. }
  164. return me.renderTile();
  165. },
  166. /**
  167. * Method: startTransition
  168. * Creates a backbuffer tile (if it does not exist already)
  169. * and then displays this tile.
  170. *
  171. * Parameters:
  172. * drawTile - {<Boolean>} Should the tile be drawn?
  173. */
  174. startTransition: function(drawTile) {
  175. // <SuperMap.CanvasLayer.> takes care about the transition
  176. },
  177. /**
  178. * Method: renderTile
  179. * Creates the canvas element and sets the URL.
  180. *
  181. * Returns:
  182. * {Boolean} Always returns true.
  183. */
  184. renderTile: function() {
  185. var me = this;
  186. me.url = me.layer.getURL(me.bounds);
  187. me.positionImage();
  188. return true;
  189. },
  190. /**
  191. * Method: createImage
  192. * Creates the image and starts loading it.
  193. */
  194. createImage: function() {
  195. //如果在缓存的内存图片可以找到的话,就不再创建图片,直接调用图片的onLoad事件的响应函数
  196. //不过这里需要注意,制作专题图的时候,需要清除已经缓存的内存图片
  197. //否则有可能看不到专题图的图片。
  198. if (this.lastImage !== null && !this.lastImage.complete) {
  199. // 平移多次时候将不会请求图片,chrome下不能用。 https://bugs.webkit.org/show_bug.cgi?id=35377
  200. this.lastImage.src = '';
  201. }
  202. //先查看内存中是否有保存
  203. var me = this, image = me.layer.getMemoryImg(me.bounds);
  204. me.lastBounds = me.bounds.clone();
  205. if (image) {
  206. me.newImgTag = "";
  207. //找到就显示
  208. me.lastImage = image;
  209. me.layer.drawCanvasTile(image, me.position);
  210. if(me.firstInView){
  211. me.setFirstInView();
  212. }
  213. } else {
  214. //构造新的image对象
  215. var key = me.layer.getXYZ(me.bounds);
  216. //如果使用andriod加载,则需本地加载完成后重新设置url。否则直接加载
  217. if (!SuperMap.isApp) {
  218. me.newImgTag = key.x + "_" + key.y + "_" + key.z;
  219. me.loadTileImage();
  220. } else{
  221. var strX = key.x,
  222. strY = key.y,
  223. strZ;
  224. if(me.layer instanceof SuperMap.Layer.CloudLayer || me.layer.storageType=="db"){
  225. strZ = key.z;
  226. } else{
  227. strZ = me.layer.scales[key.z].toExponential();
  228. }
  229. var canvasImageContext = {
  230. tile: me,
  231. X: strX,
  232. Y: strY,
  233. Z: strZ,
  234. viewRequestID: me.layer.map.viewRequestID
  235. };
  236. me.newImgTag = strX + "_" + strY + "_" + strZ;
  237. // var saveUrlProxy = function() {
  238. // this.tile.onLoadsaveUrlFunction(this);
  239. // }
  240. me.lastImage = new Image();
  241. var methodName = me.getMethodName();
  242. var callBack = function(canvasImageContext,methodName){
  243. return function(r){
  244. window[methodName] = null;
  245. canvasImageContext.tile.onLoadsaveUrlFunction(canvasImageContext,r);
  246. }
  247. }(canvasImageContext,methodName);
  248. window[methodName] = callBack;
  249. /*window.plugins.localstoragemanager.getImg(me.url, me.layer.name, strX, strY, strZ,methodName,
  250. function(){},
  251. function(e){//errorfunction
  252. }
  253. ); */
  254. cordova.exec(function(){}, function(e){}, "LocalStoragePlugin","getImg", [me.url, me.layer.name, strX, strY, strZ,methodName]);
  255. }
  256. }
  257. },
  258. getMethodName:function(){
  259. var dateTime=new Date();
  260. var yy=dateTime.getFullYear();
  261. var MM1=dateTime.getMonth()+1; //因为1月这个方法返回为0,所以加1
  262. var dd=dateTime.getDate();
  263. var hh=dateTime.getHours();
  264. var mm=dateTime.getMinutes();
  265. var ss=dateTime.getSeconds();
  266. var ms = dateTime.getMilliseconds();
  267. var name = "getImgFromLocal_"+yy+MM1+dd+hh+mm+ss+ms+(Math.round(Math.random()*10000));
  268. return name;
  269. },
  270. //重置本地URL,加载图片
  271. onLoadsaveUrlFunction:function(canvasImageContext, r) {
  272. var me = this;
  273. var nowImgTag = r.x + "_" + r.y + "_" + r.z;
  274. if(me.newImgTag != nowImgTag){
  275. return;
  276. }
  277. if(r.data){
  278. if(r.data=="null"){
  279. return false;
  280. }
  281. var src = "data:image/jpeg;base64,"+r.data;
  282. }
  283. else{
  284. var src = me.layer.sdcardPath + "SuperMap/" + me.layer.name + "/" +
  285. canvasImageContext.Z + "/" + canvasImageContext.X + "_" + canvasImageContext.Y + ".png";
  286. }
  287. me.url = src;
  288. me.loadTileImage();
  289. },
  290. /**
  291. * Method: loadTileImage
  292. * 通过url加载图片,并对加载后的图片做缓存处理
  293. */
  294. loadTileImage: function(){
  295. var me = this,
  296. image = new Image();
  297. image.firstInView = true;
  298. me.lastImage = image;
  299. var context = {
  300. image: image,
  301. tile: me,
  302. viewRequestID: me.layer.map.viewRequestID,
  303. newImgTag: me.newImgTag
  304. //bounds: me.bounds.clone()// todo: do we still need the bounds? guess no
  305. //urls: this.layer.url.slice() // todo: for retries?
  306. };
  307. var onLoadFunctionProxy = function() {
  308. if(this.tile.newImgTag == this.newImgTag){
  309. this.tile.onLoadFunction(this);
  310. }
  311. };
  312. var onErrorFunctionProxy = function() {
  313. this.tile.onErrorFunction(this);
  314. };
  315. image.onload = SuperMap.Function.bind(onLoadFunctionProxy, context);
  316. image.onerror = SuperMap.Function.bind(onErrorFunctionProxy, context);
  317. //图片的url赋予给image后就会从服务器获取到图片
  318. image.src = me.url;
  319. },
  320. /**
  321. * Method: positionImage
  322. * Sets the position and size of the tile's frame and
  323. * canvas element.
  324. */
  325. positionImage: function() {
  326. var me = this;
  327. // if the this layer doesn't exist at the point the image is
  328. // returned, do not attempt to use it for size computation
  329. if (!me.layer) {
  330. return;
  331. }
  332. me.createImage();
  333. },
  334. /**
  335. * Method: onLoadFunction
  336. * Called when an image successfully finished loading. Draws the
  337. * image on the canvas. 图片加载成功后在canvas上进行绘制
  338. *
  339. * Parameters:
  340. * context - {<Object>} The context from the onload event.
  341. */
  342. onLoadFunction: function(context) {
  343. if ((this.layer === null) ||
  344. (context.viewRequestID !== this.layer.map.viewRequestID) ||
  345. (context.image !== this.lastImage)) {
  346. return;
  347. }
  348. this.canvasContext = null;
  349. var image = context.image;
  350. if(context.tile.shouldDraw){
  351. //绘制图片
  352. this.displayImage(image,context.newImgTag);
  353. }
  354. //保存图片缓存
  355. this.layer.addMemoryImg(this.lastBounds, image, context);
  356. },
  357. /**
  358. * APIMethod: drawImgData
  359. *
  360. * 重新绘制进行像素操作后的imgdata
  361. *
  362. * Parameters:
  363. * imgData - {<String>} canvas.toDataURL()得到的图片字符串
  364. * evt - {<Object>} tileloaded事件返回的事件对象,layer.events.on({tileloaded: function(evt) {}})
  365. */
  366. drawImgData:function(imgData,evt){
  367. var m,idx=evt.idx;
  368. m = new Image();
  369. m.onload = function(me,m,idx){
  370. return function(){
  371. if(idx==me.newImgTag){
  372. //m.firstInView = true;
  373. me.lastImage = m;
  374. me.layer.drawCanvasTile(m, me.position);
  375. }
  376. }
  377. }(this,m,idx);
  378. if(idx==this.newImgTag){
  379. m.src = imgData;
  380. }
  381. },
  382. /**
  383. * Method: displayImage
  384. * Takes care of resizing the canvas and then draws the
  385. * canvas.
  386. *
  387. * Parameters:
  388. * image - {Image/Canvas} The image to display
  389. * idx - {String} tile的标记
  390. */
  391. displayImage: function(image,idx) {
  392. var me = this,
  393. layer = me.layer;
  394. if (layer.canvasFilter && !image.filtered) {
  395. // if a filter is set, apply the filter first and
  396. // then use the result
  397. me.filter(image);
  398. return;
  399. }
  400. if(image.firstInView){
  401. me.setFirstInView();
  402. }
  403. //绘制图片
  404. layer.fixPosition();
  405. // layer.drawCanvasTile(image, me.position);
  406. //更新图片状态
  407. me.isLoading = false;
  408. me.events.triggerEvent("loadend",{"idx":idx});
  409. },
  410. /**
  411. * Method: onErrorFunction
  412. * Called when an image finished loading, but not successfully.
  413. *
  414. * Parameters:
  415. * context - {<Object>} The context from the onload event.
  416. */
  417. onErrorFunction: function(context) {
  418. var me = this;
  419. //图片请求失败,就不绘这个tile,防止调用canvasContext.drawImage方法出错。
  420. if (context.image !== me.lastImage) {
  421. /* Do not trigger 'loadend' when a new image was request
  422. * for this tile, because then 'reload' was triggered instead
  423. * of 'loadstart'.
  424. * If we would trigger 'loadend' now, Grid would get confused about
  425. * its 'numLoadingTiles'.
  426. */
  427. return;
  428. }
  429. //retry? with different url?
  430. me._attempts = (me._attempts) ? (me._attempts + 1) : 1;
  431. if (me._attempts <= SuperMap.IMAGE_RELOAD_ATTEMPTS) {
  432. if (me.layer.url && SuperMap.Util.isArray(me.layer.url) && me.layer.url.length > 1){
  433. me.layer._attempts = me._attempts;
  434. me.draw();
  435. return ;
  436. }
  437. }else
  438. {
  439. me._attempts = 0;
  440. }
  441. me.events.triggerEvent("loadend");
  442. },
  443. /**
  444. * Method: setFirstInView
  445. *
  446. */
  447. setFirstInView: function(){
  448. var me = this;
  449. if(!me.fadingTimer){
  450. var context = {
  451. canvasImage:me,
  452. image: me.lastImage
  453. };
  454. me.fadingTimer = window.setTimeout(SuperMap.Function.bind(me.setNotFirstInView, context),100);
  455. }
  456. },
  457. /**
  458. * Method: setNotFirstInView
  459. *
  460. */
  461. setNotFirstInView: function(){
  462. var me = this;
  463. // me.lastImage.firstInView = false;
  464. me.image.firstInView = false;
  465. window.clearTimeout(me.canvasImage.fadingTimer);
  466. me.canvasImage.fadingTimer = null;
  467. me.canvasImage.displayImage(me.image);
  468. },
  469. /**
  470. * Method: show
  471. * Show the tile. Called in <SuperMap.Tile.showTile()>.
  472. */
  473. show: function() {},
  474. /**
  475. * Method: hide
  476. * Hide the tile. To be implemented by subclasses (but never called).
  477. */
  478. hide: function() { },
  479. /**
  480. * Method: isTooBigCanvas
  481. * Used to avoid that the backbuffer canvas gets too big when zooming in very fast.
  482. * Otherwise drawing the canvas would take too long and lots of memory would be
  483. * required.
  484. */
  485. isTooBigCanvas: function(size) {
  486. return size.w > 5000;
  487. },
  488. /**
  489. * Method: moveTo
  490. *
  491. */
  492. moveTo: function (bounds, position, redraw) {
  493. if (redraw == null) {
  494. redraw = true;
  495. }
  496. this.bounds = bounds.clone();
  497. this.position = position.clone();
  498. //设置不重置canvas
  499. this.layer.redrawCanvas = false;
  500. if (redraw) {
  501. this.draw();
  502. }
  503. },
  504. CLASS_NAME: "SuperMap.Tile.WebGLImage"
  505. });