grinat / leaflet-simple-map-screenshoter

Leaflet plugin which take screenshot of map
MIT License
74 stars 19 forks source link

How would I use this with angular? #5

Closed AndrewRogers1994 closed 4 years ago

AndrewRogers1994 commented 4 years ago

Hi there, I was wondering if you would be able to provide some help on how to use this with angular?

i've tried adding some custom defininitions to the typings however this is my first time doing this sort of thing and I'm having some issues

here's what I have for my typings:

/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
  id: string;
}

import * as L from 'leaflet';
declare module 'leaflet' {
  namespace control {
    function browserPrint(options?: any): Control.BrowserPrint;
    function takeScreen(format:any, overridedPluginOptions:any) : simpleMapScreenshoter;
    interface simpleMapScreenshoter {
        addTo(map:L.Map) :any;
    }
  }
  namespace Control {
    interface BrowserPrint {
      addTo(map: L.Map): any;
    }
  }
}

Thanks in advance

grinat commented 4 years ago

I haven't tested this in Angular, but it work correct in Vue Typescript: Short way:

declare module "leaflet" {
  export function simpleMapScreenshoter(options?: any): any;
}

Long way:

import {Control} from 'leaflet'

interface SimpleMapScreenshoter extends Control{
  takeScreen(): Promise<File>;
}

interface SimpleMapScreenshoterOptions {
  screenName(): string;
  // other options from https://github.com/grinat/leaflet-simple-map-screenshoter/blob/master/src/SimpleMapScreenshoter.js#L9
}

declare module "leaflet" {
  export function simpleMapScreenshoter(options?: SimpleMapScreenshoterOptions): SimpleMapScreenshoter;
}
AndrewRogers1994 commented 4 years ago

Hi thanks for your quick response, I've tried your short way and I get this error at runtime

leaflet__WEBPACK_IMPORTED_MODULE_4__.simpleMapScreenshoter is not a function

Here's some code snippets for what I have

typings.d.ts

/* SystemJS module definition */
declare var module: NodeModule;
interface NodeModule {
  id: string;
}

import * as L from 'leaflet';
declare module 'leaflet' {
     export function simpleMapScreenshoter(options?: any): any;
}

Component.ts

import * as L from 'leaflet';

export class AddPostComponent implements OnInit, AfterViewChecked {
simpleMapScreenshoter:any;
map:any;

onMapReady(map: L.Map, detail: any) 
{
      this.simpleMapScreenshoter =  L.simpleMapScreenshoter().addTo(this.map);
        this.map = map;
        this.takeScreenshot();
}

takeScreenshot()
{
  let format = 'image' // 'image' - return base64, 'canvas' - return canvas
  let overridedPluginOptions = 
  {
   mimeType: 'image/jpeg'
  }

  this.simpleMapScreenshoter.takeScreen(format, overridedPluginOptions).then(blob => 
  {
    alert('done')
    console.log(blob);
  }).catch(e => 
  {
    console.error(e)
   })
}
}
grinat commented 4 years ago

Webpack cant find plugin because you dont import it

In Component.ts

import * as L from 'leaflet'
// import plugin after leaflet
import 'leaflet-simple-map-screenshoter'
AndrewRogers1994 commented 4 years ago

Edit: This issue was because I was passing the wrong variable

AndrewRogers1994 commented 4 years ago

It all seems to run through and the alert is called, however the base64 output does not display the entire map (Please see attatched image)

Base64:

data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCgsOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD/2wBDAQMDAwQDBAgEBAgQCwkLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBD/wAARCAEoAC8DASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAj/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AL4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB//Z
grinat commented 4 years ago

Thanks again for such a quick response, That error has gone away and is now replaced with this one:

 Cannot read property '_controlCorners' of undefined
    at NewClass.addTo (leaflet-src.js:4786)
    at Component.push../src/app/Components/component.component.ts.Component.onMapReady (component.component.ts:2467)
    at Object.eval [as handleEvent] (Component.html:757)
    at handleEvent (core.js:23107)
    at callWithDebugContext (core.js:24177)
    at Object.debugHandleEvent [as handleEvent] (core.js:23904)
    at dispatchEvent (core.js:20556)
    at core.js:22046
    at SafeSubscriber.schedulerFn [as _next] (core.js:13527)
    at SafeSubscriber.push../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (Subscriber.js:192)

Then you call L.simpleMapScreenshoter().addTo(this.map) map is undefined

In leafltet sources:

addTo: function (map) {
       ...
            corner = map._controlCorners[pos];
AndrewRogers1994 commented 4 years ago

In leafltet sources:

addTo: function (map) {
       ...
          corner = map._controlCorners[pos];

Is this response in regards to the new commented I added above (About only parts of the image being rendered?)

screen (1)

grinat commented 4 years ago

In leafltet sources:

addTo: function (map) {
       ...
            corner = map._controlCorners[pos];

Is this response in regards to the new commented I added above (About only parts of the image being rendered?)

screen (1)

No. Hmm... only parts of the image bacuse wrong width/height size of html node with map. Check your css code(maybe you forget import leaflet map css styles, maybe you create before map node calc with/height, call https://leafletjs.com/reference-1.6.0.html#map-invalidatesize). Try to set options cropImageByInnerWH to false

 L.simpleMapScreenshoter({
   cropImageByInnerWH: false
})
AndrewRogers1994 commented 4 years ago

I was able to get it all working (It was an issue with my custom css that used translateX), the only issue I'm having now is that if I call screenshot inside of onMapReady the image I get is blank.

download

However If I wait 0.5 seconds before doing this then the image is fine, any suggestions on what I could do so I don't have to wait a predefined amount of time?

download (1)

grinat commented 4 years ago

Tiles not loaded, need:

  1. wait for all tiles donwload (https://leafletjs.com/reference-1.6.0.html#gridlayer-load)
  2. wait fo tile render and wait a bit more than 0.2 secs (the duration of the tile animation fade-in)
  3. take screen

Example:

    const tiles = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png');
    tiles.on('load', () => {
        setTimeout(() => {
            simpleMapScreenshoter.takeScreen('image').then(image => {
                var img = document.createElement('img')
                img.src = image
                document.body.appendChild(img)
            })
        }, 201)
    })
AndrewRogers1994 commented 4 years ago

Thanks soo much for your continued support, I seem to have everything I need to accomplish what I was trying to do