Skip to content

Commit

Permalink
Add export function
Browse files Browse the repository at this point in the history
  • Loading branch information
russss committed May 14, 2024
1 parent 78532c2 commit 4bb9702
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 2 deletions.
1 change: 1 addition & 0 deletions web/src/distancemeasure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ class DistanceMeasure implements maplibregl.IControl {
type: 'button',
'aria-label': 'DistanceMeasure',
'aria-pressed': 'false',
title: 'Measure distance',
})
this._geojson = {
type: 'FeatureCollection',
Expand Down
31 changes: 31 additions & 0 deletions web/src/export/export.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
button.export {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24'%3E%3Cpath d='M11.25 9.331V.75a.75.75 0 0 1 1.5 0v8.58l1.949-2.11A.75.75 0 1 1 15.8 8.237l-3.25 3.52a.75.75 0 0 1-1.102 0l-3.25-3.52A.75.75 0 1 1 9.3 7.22l1.949 2.111Z'/%3E%3Cpath d='M2.5 3.75v11.5c0 .138.112.25.25.25h18.5a.25.25 0 0 0 .25-.25V3.75a.25.25 0 0 0-.25-.25h-5.5a.75.75 0 0 1 0-1.5h5.5c.966 0 1.75.784 1.75 1.75v11.5A1.75 1.75 0 0 1 21.25 17h-6.204c.171 1.375.805 2.652 1.769 3.757A.752.752 0 0 1 16.25 22h-8.5a.75.75 0 0 1-.566-1.243c.965-1.105 1.599-2.382 1.77-3.757H2.75A1.75 1.75 0 0 1 1 15.25V3.75C1 2.784 1.784 2 2.75 2h5.5a.75.75 0 0 1 0 1.5h-5.5a.25.25 0 0 0-.25.25ZM10.463 17c-.126 1.266-.564 2.445-1.223 3.5h5.52c-.66-1.055-1.098-2.234-1.223-3.5Z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-size: 18px 18px;
background-position: center;
}

div.progressWrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.2);
z-index: 10000;
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
}

div.progressWindow {
width: 30%;
text-align: center;
border: 1px solid #aaa;
background-color: white;
padding: 30px;
border-radius: 3px;
box-shadow: 3px 4px rgba(0, 0, 0, 0.2);
font-weight: bold;
}
79 changes: 79 additions & 0 deletions web/src/export/export.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import maplibregl from 'maplibre-gl'
import './export.css'
import { el, mount, unmount } from 'redom'

function calculateDim(currentZoom: number, currentWidth: number, targetZoom: number): number {
return Math.round(currentWidth * Math.pow(2, targetZoom - currentZoom))
}

class ExportControl implements maplibregl.IControl {
_map?: maplibregl.Map
exportZoom: number = 18
pixelRatio: number = 4

doExport() {
const srcContainer = this._map!._container
let width = srcContainer.clientWidth,
height = srcContainer.clientHeight
const zoom = this._map!.getZoom()

// Rescale the pixel size of the container so that it covers the same
// area at the target zoom level.
// The eventual pixel size of the exported image is dictated by the pixelRatio passed into MapLibreGL
if (this.exportZoom > this._map!.getZoom()) {
width = calculateDim(zoom, width, this.exportZoom)
height = calculateDim(zoom, height, this.exportZoom)
}

const destContainer = el('div', { style: { width: width + 'px', height: height + 'px' } })
const wrapper = el('div', destContainer, { style: { visibility: 'hidden' } })
mount(document.body, wrapper)

const progressWrapper = el(
'div.progressWrapper',
el('div.progressWindow', 'Saving map as image, please wait...')
)
mount(document.body, progressWrapper)

const renderMap = new maplibregl.Map({
container: destContainer,
style: this._map!.getStyle(),
center: this._map!.getCenter(),
pixelRatio: this.pixelRatio,
// The maximum size may be limited by the graphics card.
maxCanvasSize: [16384, 16384],
zoom: this.exportZoom,
// NB: bearing and pitch not supported
interactive: false,
preserveDrawingBuffer: true,
fadeDuration: 0,
attributionControl: false,
})

renderMap.once('idle', () => {
const canvas = renderMap.getCanvas()
const a = el('a', { href: canvas.toDataURL(), download: 'map.png' })
a.click()
a.remove()
renderMap.remove()
unmount(document.body, wrapper)
unmount(document.body, progressWrapper)
})
}

onAdd(map: maplibregl.Map): HTMLElement {
this._map = map
const button = el('button.export', {
type: 'button',
title: 'Download image',
})
button.onclick = () => this.doExport()
return el('div', button, { class: 'maplibregl-ctrl maplibregl-ctrl-group' })
}

onRemove(): void {
this._map = undefined
}
}

export default ExportControl
1 change: 1 addition & 0 deletions web/src/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ body {
width: 100%;
height: 100%;
margin: 0px;
overflow: hidden;
}
header {
display: block;
Expand Down
8 changes: 7 additions & 1 deletion web/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ContextMenu from './contextmenu'
import VillagesEditor from './villages'
import { roundPosition } from './util'
import InstallControl from './installcontrol'
import ExportControl from './export/export'

if (import.meta.env.DEV) {
map_style.sources.villages.data = 'http://localhost:2342/api/villages.geojson'
Expand Down Expand Up @@ -81,6 +82,12 @@ class EventMap {
this.map.addControl(new InstallControl(), 'top-left')

this.map.addControl(new VillagesEditor('villages', 'villages_symbol'), 'top-right')

// Display edit control only on browsers which are likely to be desktop browsers
if (window.matchMedia('(min-width: 600px)').matches) {
this.map.addControl(new ExportControl(), 'top-right')
}

this.map.addControl(this.layer_switcher, 'top-right')
this.url_hash.enable(this.map)

Expand All @@ -106,7 +113,6 @@ class EventMap {
}

const em = new EventMap()
window.em = em

if (document.readyState != 'loading') {
em.init()
Expand Down
8 changes: 7 additions & 1 deletion web/src/villages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class VillagesLayer {
}
this.popup = null

this.button = el('button')
this.button = el('button', { title: 'Place village' })
this._wrapper = el('div', this.button, {
class: 'maplibregl-ctrl maplibregl-ctrl-group villages-ctrl',
style: 'display:none',
Expand Down Expand Up @@ -107,6 +107,12 @@ class VillagesLayer {
return this._wrapper
}

onRemove() {
if (this.popup) {
this.popup.remove()
}
}

createForm() {
if (!this._map || !this.villages) return
const editor = new PlaceVillageDialog(this._map, this.villages)
Expand Down

0 comments on commit 4bb9702

Please sign in to comment.