-
-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add image control to download and print map #202
base: 2.x
Are you sure you want to change the base?
Changes from all commits
ffae237
2cfba21
87f1c13
fc4f2fc
c5ba759
47ef5ca
77f575a
6b71469
8191595
bbed4c9
408c9c1
7c9d963
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import Snapshot from '../control/Snapshot/Snapshot'; | ||
|
||
export default { | ||
attach(instance) { | ||
|
||
// Create the Snapshot control and add it to the map. | ||
const control = new Snapshot(); | ||
instance.map.addControl(control); | ||
}, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
.ol-snapshot.ol-control { | ||
top: 0.5em; | ||
right: 6.5em; | ||
} | ||
|
||
.ol-snapshot.ol-control .download, | ||
.ol-snapshot.ol-control .print { | ||
display: none; | ||
} | ||
|
||
.ol-snapshot.ol-control.active .download, | ||
.ol-snapshot.ol-control.active .print { | ||
display: block; | ||
} | ||
|
||
.ol-snapshot.ol-control button { | ||
display: inline-block; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
import Control from 'ol/control/Control'; | ||
import { CLASS_CONTROL, CLASS_UNSELECTABLE } from 'ol/css'; | ||
import EventType from 'ol/events/EventType'; | ||
import MapEventType from 'ol/MapEventType'; | ||
import './Snapshot.css'; | ||
import printJS from 'print-js'; | ||
|
||
/** | ||
* @classdesc | ||
* OpenLayers Snapshot Control. | ||
* | ||
* @api | ||
*/ | ||
class Snapshot extends Control { | ||
|
||
/** | ||
* @param {Options=} opts Snapshot options. | ||
*/ | ||
constructor(opts) { | ||
const options = opts || {}; | ||
|
||
// Call the parent control constructor. | ||
super({ | ||
element: document.createElement('div'), | ||
target: options.target, | ||
}); | ||
|
||
// Create the snapshot button element. | ||
const className = options.className || 'ol-snapshot'; | ||
const button = document.createElement('button'); | ||
button.innerHTML = options.label || '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M4.8 15.2v-1.6a.8.8 0 0 0-1.6 0v1.6a.8.8 0 0 0 1.6 0M8 4a.8.8 0 0 0 .8.8h1.6a.8.8 0 0 0 0-1.6H8.8A.8.8 0 0 0 8 4m-4 7.2a.8.8 0 0 0 .8-.8V8.8a.8.8 0 0 0-1.6 0v1.6a.8.8 0 0 0 .8.8m14.4-6.4h.8v.8a.8.8 0 0 0 1.6 0v-.8a1.6 1.6 0 0 0-1.6-1.6h-.8a.8.8 0 0 0 0 1.6M12.8 4a.8.8 0 0 0 .8.8h1.6a.8.8 0 0 0 0-1.6h-1.6a.8.8 0 0 0-.8.8M5.6 19.2h-.8v-.8a.8.8 0 0 0-1.6 0v.8a1.6 1.6 0 0 0 1.6 1.6h.8a.8.8 0 0 0 0-1.6M4.8 5.6v-.8h.8a.8.8 0 0 0 0-1.6h-.8a1.6 1.6 0 0 0-1.6 1.6v.8a.8.8 0 0 0 1.6 0m14.4 3.2v1.601a.8.8 0 0 0 1.6 0V8.8a.8.8 0 0 0-1.6 0m.8 4h-1.411a1.6 1.6 0 0 1-1.431-.885l-.137-.274a.8.8 0 0 0-.715-.442h-3.811a.8.8 0 0 0-.715.442l-.137.274a1.6 1.6 0 0 1-1.432.885H8.8a.8.8 0 0 0-.8.8V20a.8.8 0 0 0 .8.8H20a.8.8 0 0 0 .8-.8v-6.4a.8.8 0 0 0-.8-.8M14.4 20a3.2 3.2 0 1 1 0-6.4 3.2 3.2 0 0 1 0 6.4"/><path d="M16 16.8a1.6 1.6 0 0 1-1.6 1.6 1.6 1.6 0 0 1-1.6-1.6 1.6 1.6 0 0 1 3.2 0"/></svg>'; | ||
button.title = options.tooltip || 'Snapshot'; | ||
button.className = className; | ||
button.type = 'button'; | ||
|
||
// Register a click event on the button. | ||
button.addEventListener(EventType.CLICK, this.captureImage.bind(this), false); | ||
|
||
// Add the button and CSS classes to the control element. | ||
const { element } = this; | ||
element.className = `${className} ${CLASS_UNSELECTABLE} ${CLASS_CONTROL}`; | ||
element.appendChild(button); | ||
|
||
// Create a download button with link. | ||
const link = document.createElement('a'); | ||
link.setAttribute('download', document.title); | ||
link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M22,16 L22,20 C22,21.1045695 21.1045695,22 20,22 L4,22 C2.8954305,22 2,21.1045695 2,20 L2,16 L4,16 L4,20 L20,20 L20,16 L22,16 Z M13,12.5857864 L16.2928932,9.29289322 L17.7071068,10.7071068 L12,16.4142136 L6.29289322,10.7071068 L7.70710678,9.29289322 L11,12.5857864 L11,2 L13,2 L13,12.5857864 Z" fill-rule="evenodd"/></svg>'; | ||
this.link = link; | ||
const download = document.createElement('button'); | ||
download.title = 'Download snapshot'; | ||
download.className = 'download'; | ||
download.appendChild(link); | ||
element.appendChild(download); | ||
|
||
// Create a print button. | ||
const print = document.createElement('button'); | ||
print.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M16.828 8.625H6.891a.703.703 0 0 1-.703-.703V2.953a.703.703 0 0 1 .703-.703h9.938a.703.703 0 0 1 .703.703v4.969a.703.703 0 0 1-.703.703M7.595 7.219h8.531V3.656H7.594Zm9.233 13.875H6.891a.703.703 0 0 1-.703-.703v-7.637a.703.703 0 0 1 .703-.703h9.938a.703.703 0 0 1 .703.703v7.637a.703.703 0 0 1-.703.703m-9.234-1.406h8.531v-6.231H7.594Z"/><path d="M19.09 17.766h-2.262a.703.703 0 0 1 0-1.406h2.262a.886.886 0 0 0 .885-.885V9.51a.886.886 0 0 0-.885-.885H4.629a.886.886 0 0 0-.885.885v5.964a.886.886 0 0 0 .885.885h2.262a.703.703 0 0 1 0 1.406H4.629a2.294 2.294 0 0 1-2.291-2.291V9.51a2.294 2.294 0 0 1 2.291-2.291H19.09a2.294 2.294 0 0 1 2.291 2.292v5.964a2.294 2.294 0 0 1-2.291 2.291"/><path d="m6.141 11.063-.069-.003a1 1 0 0 1-.135-.027l-.065-.023-.062-.03-.06-.035q-.029-.019-.055-.041a.7.7 0 0 1-.099-.099l-.041-.055q-.019-.029-.035-.059a1 1 0 0 1-.053-.127c-.007-.022-.012-.047-.017-.067s-.008-.047-.01-.068a1 1 0 0 1 0-.141 1 1 0 0 1 .027-.135l.023-.065q.013-.032.03-.062c.017-.03.022-.04.035-.059l.041-.055a1 1 0 0 1 .099-.099q.027-.022.055-.041l.06-.035q.03-.016.062-.03t.065-.023c.033-.01.047-.012.067-.017s.047-.008.068-.01a1 1 0 0 1 .138 0 .7.7 0 0 1 .135.027l.065.023.062.03.06.035q.029.019.055.041c.026.022.035.03.052.047s.031.034.047.052.028.037.041.055.024.039.035.059.021.041.03.062.016.043.023.065.012.047.017.067.008.047.01.068a1 1 0 0 1 0 .141 1 1 0 0 1-.027.135l-.023.065q-.013.032-.03.062c-.017.03-.022.04-.035.059a.8.8 0 0 1-.14.154q-.027.022-.055.041l-.06.035q-.03.016-.062.03t-.065.023c-.033.01-.044.012-.067.017s-.047.008-.068.01zm2.062 0q-.035 0-.069-.003c-.034-.003-.047-.006-.068-.01s-.047-.01-.067-.017l-.065-.023q-.032-.013-.062-.03c-.03-.017-.04-.022-.059-.035l-.055-.041a1 1 0 0 1-.099-.099q-.022-.027-.041-.055l-.036-.06-.029-.062q-.013-.032-.023-.065c-.01-.033-.012-.047-.017-.067s-.008-.047-.01-.068a1 1 0 0 1 0-.141 1 1 0 0 1 .027-.135l.023-.065q.013-.032.029-.062l.036-.06q.019-.029.041-.055a.7.7 0 0 1 .099-.099l.055-.041q.029-.019.059-.035a1 1 0 0 1 .127-.053c.022-.007.047-.012.067-.017s.047-.008.068-.01a1 1 0 0 1 .141 0 1 1 0 0 1 .135.027l.065.023q.032.013.062.03c.03.017.04.022.06.035s.038.026.055.041.035.03.052.047.031.034.047.052.028.037.041.055l.035.06q.016.03.03.062t.023.065c.01.033.012.047.017.067s.008.047.01.068a1 1 0 0 1 0 .141 1 1 0 0 1-.027.135l-.023.065-.03.062-.035.06a.8.8 0 0 1-.14.154q-.027.022-.055.041c-.028.019-.039.024-.06.035a1 1 0 0 1-.127.053c-.022.007-.047.012-.067.017a.5.5 0 0 1-.139.013m6.421 5.062H9.094a.703.703 0 0 1 0-1.406h5.531a.703.703 0 0 1 0 1.406m0 2.484H9.094a.703.703 0 0 1 0-1.406h5.531a.703.703 0 0 1 0 1.406"/></svg>'; | ||
print.title = 'Print snapshot'; | ||
print.className = 'print'; | ||
print.addEventListener('click', this.printSnapshot.bind(this)); | ||
element.appendChild(print); | ||
} | ||
|
||
/** | ||
* Callback to deactivate the snapshot control. | ||
* @private | ||
*/ | ||
deactivate() { | ||
this.element.classList.remove('active'); | ||
} | ||
|
||
/** | ||
* Callback for the snapshot button click event. | ||
* @param {MouseEvent} event The event to handle | ||
* @private | ||
*/ | ||
captureImage(event) { | ||
event.preventDefault(); | ||
|
||
// Create a new canvas element to combine multiple map canvas data to. | ||
const outputCanvas = document.createElement('canvas'); | ||
const [width, height] = this.getMap().getSize(); | ||
outputCanvas.width = width; | ||
outputCanvas.height = height; | ||
const outputContext = outputCanvas.getContext('2d'); | ||
|
||
// Draw each canvas from this map into the new canvas. | ||
// Logic for transforming and drawing canvases derived from ol export-pdf example. | ||
// https://github.com/openlayers/openlayers/blob/6f2ca3b9635f273f6fbddab834bd9126c7d48964/examples/export-pdf.js#L61-L85 | ||
Array.from(this.getMap().getTargetElement().querySelectorAll('.ol-layer canvas')) | ||
.filter(canvas => canvas.width > 0) | ||
.forEach((canvas) => { | ||
const { opacity } = canvas.parentNode.style; | ||
outputContext.globalAlpha = opacity === '' ? 1 : Number(opacity); | ||
|
||
// Get the transform parameters from the style's transform matrix. | ||
// This is necessary so that vectors align with raster layers. | ||
const { transform } = canvas.style; | ||
const matrix = transform | ||
.match(/^matrix\(([^(]*)\)$/)[1] | ||
.split(',') | ||
.map(Number); | ||
|
||
// Apply the transform to the export map context. | ||
CanvasRenderingContext2D.prototype.setTransform.apply( | ||
outputContext, | ||
matrix, | ||
); | ||
outputContext.drawImage(canvas, 0, 0); | ||
}); | ||
Comment on lines
+87
to
+110
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about this @symbioquine ? Implementing the transform + draw logic in a more declarative way. Also linked to the reference - I was torn because as a whole the example is overly complex and we only need this portion - but still good to reference 👍 |
||
|
||
// Build a jpeg data url and update link. | ||
const url = outputCanvas.toDataURL('image/jpeg'); | ||
this.link.href = url; | ||
|
||
// Remove the output canvas. | ||
outputCanvas.remove(); | ||
|
||
// Enable the snapshot actions. | ||
this.element.classList.add('active'); | ||
|
||
// Subscribe to events to deactivate snapshot actions. | ||
this.getMap().on(EventType.CLICK, this.deactivate.bind(this)); | ||
this.getMap().on(EventType.CHANGE, this.deactivate.bind(this)); | ||
this.getMap().on(MapEventType.MOVESTART, this.deactivate.bind(this)); | ||
} | ||
|
||
/** | ||
* Callback for the snapshot button click event. | ||
* @private | ||
*/ | ||
printSnapshot() { | ||
if (this.link.href.length) { | ||
printJS(this.link.href, 'image'); | ||
} | ||
} | ||
|
||
} | ||
|
||
export default Snapshot; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you do the Inkscape part of my procedure? I was able to get it down to closer to 1.8KB instead of ~2.7KB like it is here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I should have mentioned that. I did not. I had trouble getting the extensions to work, I believe due to how I installed Inkscape with Flatpak. I just had limited time and found that https://www.svgviewer.dev/ could also resize and merge paths/groups.
Do you know, was most of this decrease in size from the transform extension?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@symbioquine I just reinstalled Inkscape and got the transform extension working. I tested using the resulting Print SVG from this commit 408c9c1 (I believe the same svg referenced in your comment)
This is already sized to 24x24px so I didn't resize in Inkscape. After applying the To Absolute and Apply Transform extensions from Inkscape and opening in SVGOMG it shows that the file went from 2.63KB to 2.54KB. I don't think this Apply Transform extension is doing much because there aren't any transforms in the SVG.
However, once I play with the "Number Precision" setting in SVGOMG I found I can drastically decrease the filesize. Precision of 2 = 1.57KB and looks good, but Precision of 1 = 928B but starts to get jagged edges. I'm curious if you remember what Precision you were using. There are many settings in SVGOMG it might be hard to really standardize on all of them.