Skip to content

Commit

Permalink
Added download process
Browse files Browse the repository at this point in the history
  • Loading branch information
barak committed Aug 10, 2024
1 parent 6018c8c commit a8a491d
Show file tree
Hide file tree
Showing 31 changed files with 604 additions and 119 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ module.exports = {
],
rules: {
'vue/require-default-prop': 'off',
'vue/multi-word-component-names': 'off'
'vue/multi-word-component-names': 'off',
'vue/attribute-order': 'off',
'@typescript-eslint/no-explicit-any': 'off'
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ dist
out
.DS_Store
*.log*
map-*
12 changes: 8 additions & 4 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion .prettierrc.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
singleQuote: true
semi: false
printWidth: 100
printWidth: 220
trailingComma: none
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ This standalone portable application allows downloading maps from various source
3. Show and resize the cropping area (use the control key to toggle between dragging the map or the crop area)
4. Download your map.


![screenshot.gif](https://github.com/extic/map-downloader/blob/main/packages/renderer/public/screenshot.gif?raw=true)
![screenshot.gif](https://github.com/extic/map-downloader/blob/main/src/renderer/public/screenshot.gif?raw=true)

## Donate

If you like this application, I would very much appriciate your donation as it gives me the will to continue and improve it. Thank you!
If you like this application, I would very much appreciate your donation as it gives me the will to continue and improve it. Thank you!

## List of map sources
- GovMap - https://www.govmap.gov.il
Expand Down
24 changes: 22 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"dependencies": {
"@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0",
"electron-fetch": "^1.9.1",
"electron-updater": "^6.1.7",
"pinia": "^2.2.0",
"pureimage": "^0.4.13",
Expand Down
14 changes: 2 additions & 12 deletions src/common/maps/govmap.data.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
// import * as pimage from "pureimage"
// import { Bitmap } from "pureimage"
// import { Readable } from "stream"
import { MapData, UrlResult, UrlUsageType } from "./map.data"
import { MapData, UrlResult, UrlUsageType } from './map.data'

export const mapDataGovMap: MapData = {
name: 'GovMap',
Expand All @@ -10,7 +7,7 @@ export const mapDataGovMap: MapData = {
if (mapType === '1:25000' && (zoomLevel < 5 || zoomLevel > 9)) {
return { url: '', unsupported: true }
}
const zoomLevelStr = (mapType === '1:25000' ? zoomLevel - 5 : zoomLevel).toString(10).padStart(2, "0")
const zoomLevelStr = (mapType === '1:25000' ? zoomLevel - 5 : zoomLevel).toString(10).padStart(2, '0')
const rowStr = row.toString(16).padStart(8, '0')
const colStr = col.toString(16).padStart(8, '0')
// https://cdn.govmap.gov.il/B0BZ1ORTO23/L08/R00004987/C00004114.jpg
Expand All @@ -20,7 +17,6 @@ export const mapDataGovMap: MapData = {
const mapTypeStr = mapType === 'Satellite' ? 'B0BZ1ORTO23' : mapType === 'Street & Buildings' ? 'B0b3010BLDG' : '2024MAP25KTO'
const suffix = mapType === 'Satellite' ? 'jpg' : 'png'
const domain = 'cdn.govmap.gov.il'
console.log(`https://${domain}/${mapTypeStr}/L${zoomLevelStr}/R${rowStr}/C${colStr}.${suffix}`);
return {
url: `https://${domain}/${mapTypeStr}/L${zoomLevelStr}/R${rowStr}/C${colStr}.${suffix}`
}
Expand All @@ -41,12 +37,6 @@ export const mapDataGovMap: MapData = {
return 2
},

// decode: async (mapType: string, buffer: Buffer): Promise<Bitmap> => {
// return await (mapType === 'Satellite'
// ? pimage.decodeJPEGFromStream(Readable.from(buffer))
// : pimage.decodePNGFromStream(Readable.from(buffer)))
// },

supportedMapTypes: ['Satellite', 'Street & Buildings', '1:25000'],

showScale: true,
Expand Down
16 changes: 1 addition & 15 deletions src/common/maps/map.data.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
// import { Bitmap } from "pureimage"
import { mapDataTelAviv } from './tel-aviv.data'
import { mapDataGalilTahton } from './galil-tahton.data'
import { mapDataHaifa } from './haifa.data'
import { mapDataGovMap } from './govmap.data'
import { mapDataHodHasharon } from './hod-hasharon.data'
import { mapDataMapy } from './mapy.data'
import { mapDataNetanya } from './netanya.data'

export type ZoomLayer = {
readonly scale: number
Expand All @@ -27,17 +20,10 @@ export type UrlResult = {

export type MapData = {
name: string
urlProvider: (
usageType: UrlUsageType,
mapType: string,
zoomLevel: number,
row: number,
col: number
) => Promise<UrlResult>
urlProvider: (usageType: UrlUsageType, mapType: string, zoomLevel: number, row: number, col: number) => Promise<UrlResult>
getDownloaderHeaders?: () => any
zoomLevelProvider: (zoomLevel: number) => string
zoomFactorProvider: (zoomLevel: number, zoomIn: boolean) => number
// decode: (mapType: string, buffer: Buffer) => Promise<Bitmap>
supportedMapTypes: string[]
showScale: boolean
referer: string | undefined
Expand Down
92 changes: 92 additions & 0 deletions src/main/downloader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as pimage from 'pureimage'
import * as fs from 'fs'
import { BrowserWindow, shell } from 'electron'
import crypto from 'crypto'
import { DownloadData } from '../common/download'
import { MapData, maps, UrlResult, UrlUsageType } from '../common/maps/map.data'
import { getBackendData } from './maps/map-backend'

export const downloadOptions = {
canceled: false
}

export const downloadMap = async (win: BrowserWindow, request: DownloadData) => {
if (!request) {
return
}

downloadOptions.canceled = false

const maxX = request.endCol - request.startCol + 1
const maxY = request.endRow - request.startRow + 1

console.log(`Starting download of ${maxX * maxY} tiles`)

const map = maps.find((it) => it.name === request.mapName)!

const croppedWidth = maxX * 256 - request.startX - (256 - request.endX)
const croppedHeight = maxY * 256 - request.startY - (256 - request.endY)
const overallImg = pimage.make(croppedWidth, croppedHeight)
const overallCtx = overallImg.getContext('2d')

const backendData = getBackendData(map.name)

for (let y = 0; y < maxY; y++) {
for (let x = 0; x < maxX; x++) {
if (downloadOptions.canceled) {
console.log('Download canceled')
return
}

const progress = (x + y * maxX) / (maxX * maxY)
win.webContents.send('download-progress', progress)

console.log(`Downloading images: ${(progress * 100).toFixed(2)}%, x=${x}/${maxX}, y=${y}/${maxY}`)

const { url, unsupported } = await getTileUrl(map, request.zoomLevel, request.startRow + y, request.startCol + x, request.mapType)
try {
if (!unsupported) {
const headers = map.getDownloaderHeaders ? map.getDownloaderHeaders() : {}

const response = await fetch(url, headers) //, { responseType: "arraybuffer" });
const arrayBuffer = await response.arrayBuffer()
const buffer = Buffer.from(arrayBuffer)
const img1 = await backendData.decode(request.mapType, buffer)
const ctx = img1.getContext('2d')
const imageData = ctx.getImageData(0, 0, 256, 256)
for (let j = 0; j < 256; j++) {
const posY = j + y * 256 - request.startY
if (posY >= 0) {
for (let i = 0; i < 256; i++) {
const posX = i + x * 256 - request.startX
if (posX >= 0) {
overallCtx.fillPixelWithColor(posX, posY, imageData.getPixelRGBA(i, j))
}
}
}
}
}
} catch (error) {
console.log(error)
}
}
}

console.log(`Downloading done`)

const fileName = `map-${crypto.randomUUID()}.png`
pimage
.encodePNGToStream(overallImg, fs.createWriteStream(fileName))
.then(() => {
win.webContents.send('download-done', true)
shell.openPath(fileName)
})
.catch((e) => {
console.log('there was an error writing', e)
win.webContents.send('download-done', false)
})
}

const getTileUrl = async (map: MapData, zoomLevel: number, row: number, col: number, mapType: string): Promise<UrlResult> => {
return await map.urlProvider(UrlUsageType.DOWNLOAD, mapType, zoomLevel, row, col)
}
26 changes: 26 additions & 0 deletions src/main/event-registrar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { App, BrowserWindow, ipcMain } from 'electron'
import { downloadMap, downloadOptions } from './downloader'
import { handleHttpRequest } from './http-request.service'
import { setReferrer } from './main-store'

export const eventRegistrar = {
registerEvents: (win: BrowserWindow, app: App) => {
ipcMain.on('download-map', async (_, arg) => {
await downloadMap(win, arg)
})

ipcMain.on('cancel-download', () => {
downloadOptions.canceled = true
})

ipcMain.on('get-app-version', () => {
win.webContents.send('app-version', app.getVersion())
})

ipcMain.on('set-referer', (_, arg) => {
setReferrer(arg)
})

ipcMain.handle('http:request', handleHttpRequest)
}
}
12 changes: 12 additions & 0 deletions src/main/http-request.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { IpcMainInvokeEvent } from 'electron'
import fetch from 'electron-fetch'

export async function handleHttpRequest(_: IpcMainInvokeEvent, url: string): Promise<ArrayBuffer> {
console.log('Getting', url)
const response = await fetch(url, {
headers: {
referer: 'https://en.mapy.cz/'
}
})
return await response.arrayBuffer()
}
Loading

0 comments on commit a8a491d

Please sign in to comment.