-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Вывести слой ОКН в виде фоток в кружках (#136)
- Loading branch information
Showing
1,430 changed files
with
2,874 additions
and
1,480 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
export const VERCEL_PUBLIC_PATH = '/okn-static/'; | ||
|
||
export const VERCEL_OUTPUT_PATH = `./public${VERCEL_PUBLIC_PATH}`; | ||
|
||
export const VERCEL_PUBLIC_IMAGES_PATH = `${VERCEL_PUBLIC_PATH}images/`; | ||
|
||
export const IMAGES_URLS_PATH = `${VERCEL_OUTPUT_PATH}images/`; | ||
|
||
export const PLACEMARKS_CACHE_PATH = `${VERCEL_OUTPUT_PATH}placemarks.json`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
import https from 'node:https'; | ||
import fs from 'node:fs'; | ||
import items from '../../public/ekb-okn.json' assert { type: "json" }; | ||
import { resize, optimize } from './prepareImages.mjs'; | ||
import { IMAGES_URLS_PATH, PLACEMARKS_CACHE_PATH, VERCEL_PUBLIC_IMAGES_PATH } from './constants.mjs'; | ||
|
||
const start = Date.now() | ||
|
||
async function init() { | ||
console.log('Prepare images') | ||
|
||
await clearCachedImages(IMAGES_URLS_PATH); | ||
|
||
const imageUrls = getImagesUrls(items.features); | ||
|
||
console.log(`Download ${imageUrls.length} images`); | ||
const images = await downloadImages(imageUrls) | ||
.then(log('Resize images')) | ||
.then(resize) | ||
.then(log('Remove original size images')) | ||
.then(removeOriginalImages) | ||
.then(log('Optimize images')) | ||
.then(optimize) | ||
.catch(console.log); | ||
|
||
console.log('Save metadata') | ||
await saveMetadata(PLACEMARKS_CACHE_PATH, items.features, images) | ||
|
||
console.log(`Finish in ${(Date.now() - start) / 1000} seconds`) | ||
} | ||
init() | ||
|
||
async function clearCachedImages(path) { | ||
if (fs.existsSync(path)) { | ||
fs.rmSync(path, { recursive: true }); | ||
} | ||
|
||
fs.mkdirSync(path, { recursive: true }); | ||
} | ||
|
||
function getImagesUrls(items) { | ||
return items.reduce((all, item) => { | ||
const img = item.properties.img | ||
|
||
if (Boolean(img)) { | ||
if (typeof img === 'string') { | ||
return all.concat(img) | ||
} | ||
return all.concat(img.url) | ||
} | ||
return all; | ||
}, []); | ||
} | ||
|
||
async function downloadImages(urls) { | ||
const downloadImage = (url) => | ||
new Promise((resolve, reject) => { | ||
const ext = 'jpg'; | ||
const guid = getGUID(url); | ||
const filename = `${guid}.${ext}`; | ||
const file = fs.createWriteStream(IMAGES_URLS_PATH + filename); | ||
|
||
https.get(url, (response) => { | ||
response.pipe(file); | ||
}); | ||
|
||
file.on('finish', () => { | ||
file.close(); | ||
resolve({ id: guid, path: filename }); | ||
}); | ||
|
||
file.on('error', (e) => { | ||
console.error(`Not loaded: ${filename}`, e); | ||
reject(); | ||
}); | ||
}); | ||
|
||
return Promise.all(urls.map(downloadImage)); | ||
} | ||
|
||
function getGUID(fileUrl) { | ||
return new URL(fileUrl).pathname.split('/').at(-1).replace(/\D/g, ''); | ||
} | ||
|
||
function log(message = '', getMessage = () => '') { | ||
return (...args) => { | ||
console.log(message, getMessage(...args)); | ||
return Promise.resolve(...args); | ||
}; | ||
} | ||
|
||
async function removeOriginalImages(items) { | ||
await Promise.all( | ||
items.map( | ||
(item) => | ||
new Promise((resolve) => { | ||
try { | ||
fs.unlinkSync(IMAGES_URLS_PATH + item.path); | ||
} catch (e) { | ||
console.log(`Error remove ${e}`); | ||
} | ||
resolve(); | ||
}), | ||
), | ||
); | ||
return items; | ||
} | ||
|
||
async function saveMetadata(cachPath, items, images) { | ||
const imagesById = images.reduce((all, item) => { | ||
all[item.id] = item; | ||
return all; | ||
}, {}); | ||
|
||
const updatedItems = items.map((item) => { | ||
const img = item.properties.img | ||
|
||
if (Boolean(img)) { | ||
let guid = null; | ||
|
||
if (typeof img === 'string') { | ||
guid = getGUID(img); | ||
} | ||
guid = getGUID(img.url); | ||
const image = imagesById[guid]; | ||
|
||
if(image) { | ||
return { | ||
...item, | ||
preview: { | ||
id: image.id, | ||
m: { | ||
...image.m, | ||
src: `${VERCEL_PUBLIC_IMAGES_PATH}m_${image.path}`, | ||
}, | ||
s: { | ||
...image.s, | ||
src: `${VERCEL_PUBLIC_IMAGES_PATH}s_${image.path}`, | ||
}, | ||
} | ||
} | ||
} | ||
} | ||
|
||
return item; | ||
}); | ||
|
||
fs.writeFileSync(cachPath, JSON.stringify(updatedItems)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import sharp from 'sharp'; | ||
import imagemin from 'imagemin'; | ||
import imageminMozjpeg from 'imagemin-mozjpeg'; | ||
import imageminPngquant from 'imagemin-pngquant'; | ||
import imageminSvgo from 'imagemin-svgo'; | ||
import { IMAGES_URLS_PATH } from './constants.mjs'; | ||
|
||
export async function optimize(items) { | ||
await imagemin([`${IMAGES_URLS_PATH}/*.{jpg,jpeg,png,svg}`], { | ||
destination: IMAGES_URLS_PATH, | ||
plugins: [ | ||
imageminMozjpeg({ quality: 75 }), | ||
imageminPngquant({ quality: [0.6, 0.8] }), | ||
imageminSvgo(), | ||
], | ||
}); | ||
|
||
return items; | ||
} | ||
|
||
export async function resize(items) { | ||
const resizeAndRoundOne = (filename, maxSize, prefix) => | ||
sharp(IMAGES_URLS_PATH + filename) | ||
.composite([ | ||
{ | ||
input: Buffer.from( | ||
`<svg> | ||
<circle | ||
cx="${maxSize / 2}" | ||
cy="${maxSize / 2}" | ||
r="${maxSize / 2}" | ||
/> | ||
</svg>`, | ||
), | ||
blend: 'dest-in', | ||
}, | ||
]) | ||
.resize({ | ||
fit: sharp.fit.cover, | ||
width: maxSize, | ||
height: maxSize, | ||
}) | ||
.toFile(IMAGES_URLS_PATH + prefix + '_' + filename) | ||
.then(({ width, height }) => ({ width, height })); | ||
|
||
const resizeOne = (filename, maxSize, prefix) => | ||
sharp(IMAGES_URLS_PATH + filename) | ||
.resize({ | ||
fit: sharp.fit.inside, | ||
width: maxSize, | ||
}) | ||
.toFile(IMAGES_URLS_PATH + prefix + '_' + filename) | ||
.then(({ width, height }) => ({ width, height })); | ||
|
||
return Promise.all( | ||
items.map(async ({ id, path }) => { | ||
const m = await resizeOne(path, 800, 'm'); | ||
const s = await resizeAndRoundOne(path, 80, 's'); | ||
return { id, m, s, path }; | ||
}), | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
.marker { | ||
object-fit: cover; | ||
border: 3px solid currentColor; | ||
background: currentColor; | ||
outline: 1px solid #000; | ||
outline-offset: -4px; | ||
border-radius: 100%; | ||
min-width: 100%; | ||
min-height: 100%; | ||
cursor: pointer; | ||
transition: all 0.1s; | ||
position: relative; | ||
transform-origin: center; | ||
display: flex; | ||
} | ||
|
||
.marker:hover, | ||
.marker.marker_open { | ||
z-index: 9999 !important; | ||
} | ||
|
||
.marker:hover { | ||
transform: scale(1.4); | ||
} | ||
|
||
.marker.marker_open { | ||
transform: scale(1.7); | ||
} |
Oops, something went wrong.