offlineMap.ts 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. const OFFLINE_MAP_DIR_KEY = 'OFFLINE_MAP_DIR__';
  2. const OFFLINE_MAP_SERVER_DIR_KEY = 'OFFLINE_MAP_SERVER_DIR__';
  3. export const OFFLINE_MAP_DIR_CHANGED_EVENT = 'offline-map-dir-changed';
  4. const LOCAL_MAP_PROTOCOL = 'localmap://';
  5. function isValidOfflineMapPath(value: string) {
  6. const text = String(value || '').trim();
  7. if (!text) return false;
  8. if (text === '[object Object]') return false;
  9. if (text === 'null' || text === 'undefined') return false;
  10. return (
  11. /^[a-zA-Z]:[\\/]/.test(text) ||
  12. text.startsWith('/') ||
  13. text.startsWith('file://') ||
  14. text.startsWith(LOCAL_MAP_PROTOCOL)
  15. );
  16. }
  17. function normalizePath(value: string) {
  18. const text = String(value || '').trim();
  19. if (!isValidOfflineMapPath(text)) return '';
  20. return text.replace(/[\\/]+$/, '');
  21. }
  22. export function getOfflineMapDir() {
  23. try {
  24. const serverDir = normalizePath(window.localStorage.getItem(OFFLINE_MAP_SERVER_DIR_KEY) || '');
  25. if (serverDir) return serverDir;
  26. return normalizePath(window.localStorage.getItem(OFFLINE_MAP_DIR_KEY) || '');
  27. } catch {
  28. return '';
  29. }
  30. }
  31. export function getManualOfflineMapDir() {
  32. try {
  33. return normalizePath(window.localStorage.getItem(OFFLINE_MAP_DIR_KEY) || '');
  34. } catch {
  35. return '';
  36. }
  37. }
  38. export function getServerOfflineMapDir() {
  39. try {
  40. return normalizePath(window.localStorage.getItem(OFFLINE_MAP_SERVER_DIR_KEY) || '');
  41. } catch {
  42. return '';
  43. }
  44. }
  45. export function setOfflineMapDir(path: string) {
  46. const normalized = normalizePath(path);
  47. try {
  48. if (!normalized) {
  49. window.localStorage.removeItem(OFFLINE_MAP_DIR_KEY);
  50. window.dispatchEvent(new CustomEvent(OFFLINE_MAP_DIR_CHANGED_EVENT, { detail: '' }));
  51. return;
  52. }
  53. window.localStorage.setItem(OFFLINE_MAP_DIR_KEY, normalized);
  54. window.dispatchEvent(new CustomEvent(OFFLINE_MAP_DIR_CHANGED_EVENT, { detail: normalized }));
  55. } catch {}
  56. }
  57. export function clearOfflineMapDir() {
  58. setOfflineMapDir('');
  59. }
  60. export function setServerOfflineMapDir(path: string) {
  61. const normalized = normalizePath(path);
  62. try {
  63. if (!normalized) {
  64. window.localStorage.removeItem(OFFLINE_MAP_SERVER_DIR_KEY);
  65. window.dispatchEvent(new CustomEvent(OFFLINE_MAP_DIR_CHANGED_EVENT, { detail: getOfflineMapDir() }));
  66. return;
  67. }
  68. window.localStorage.setItem(OFFLINE_MAP_SERVER_DIR_KEY, normalized);
  69. window.dispatchEvent(new CustomEvent(OFFLINE_MAP_DIR_CHANGED_EVENT, { detail: normalized }));
  70. } catch {}
  71. }
  72. export function buildFileUrl(filePath: string) {
  73. const normalized = normalizePath(filePath).replace(/\\/g, '/');
  74. if (!normalized) return '';
  75. if (new RegExp(`^${LOCAL_MAP_PROTOCOL}`, 'i').test(normalized)) return normalized;
  76. if (/^file:\/\//i.test(normalized)) return toLocalMapUrl(normalized);
  77. if (/^[a-zA-Z]:/.test(normalized)) {
  78. return encodeURI(`${LOCAL_MAP_PROTOCOL}/${normalized}`);
  79. }
  80. return encodeURI(`${LOCAL_MAP_PROTOCOL}${normalized.startsWith('/') ? '' : '/'}${normalized}`);
  81. }
  82. export function getOfflineMapPmtilesUrl(fileName = 'china.pmtiles') {
  83. const dir = getOfflineMapDir();
  84. if (!dir) return '';
  85. return buildFileUrl(`${dir}/${fileName}`);
  86. }
  87. export function getOfflineMapFileUrl(fileName: string) {
  88. const dir = getOfflineMapDir();
  89. if (!dir || !fileName) return '';
  90. return buildFileUrl(`${dir}/${fileName}`);
  91. }
  92. export function getOfflineMapFilePath(fileName: string) {
  93. const dir = getOfflineMapDir();
  94. if (!dir || !fileName) return '';
  95. return normalizePath(`${dir}/${fileName}`);
  96. }
  97. export function toLocalMapUrl(value: string) {
  98. const text = String(value || '').trim();
  99. if (!text) return '';
  100. if (new RegExp(`^${LOCAL_MAP_PROTOCOL}`, 'i').test(text)) return text;
  101. if (/^file:\/\//i.test(text)) {
  102. try {
  103. const url = new URL(text);
  104. const pathname = decodeURIComponent(url.pathname || '');
  105. const normalized = /^[\\/][a-zA-Z]:/.test(pathname) ? pathname.slice(1) : pathname;
  106. return buildFileUrl(normalized);
  107. } catch {
  108. return text;
  109. }
  110. }
  111. if (/^[a-zA-Z]:/.test(text) || text.startsWith('/')) {
  112. return buildFileUrl(text);
  113. }
  114. return text;
  115. }
  116. export class ElectronPmtilesSource {
  117. filePath: string;
  118. constructor(filePath: string) {
  119. this.filePath = normalizePath(filePath);
  120. }
  121. getKey() {
  122. return this.filePath;
  123. }
  124. async getBytes(offset: number, length: number) {
  125. const ipcRenderer = (window as any)?.electron?.ipcRenderer;
  126. if (!ipcRenderer?.invoke || !this.filePath || !length) {
  127. return { data: new ArrayBuffer(0) };
  128. }
  129. const payload =
  130. (await ipcRenderer.invoke('controller/os/readBinaryFileRange', {
  131. filePath: this.filePath,
  132. offset,
  133. length,
  134. })) || {};
  135. const base64 = String(payload?.data || '');
  136. const size = Number(payload?.size || 0);
  137. if (!base64 || !size) {
  138. return { data: new ArrayBuffer(0) };
  139. }
  140. const binary = window.atob(base64);
  141. const bytes = new Uint8Array(binary.length);
  142. for (let index = 0; index < binary.length; index += 1) {
  143. bytes[index] = binary.charCodeAt(index);
  144. }
  145. return { data: bytes.buffer };
  146. }
  147. }