Skip to content
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

Problem to generate the image of map (Google Maps) #345

Open
DanielSBelo opened this issue Mar 6, 2014 · 54 comments
Open

Problem to generate the image of map (Google Maps) #345

DanielSBelo opened this issue Mar 6, 2014 · 54 comments

Comments

@DanielSBelo
Copy link

hi, guys.
I need to generate a image of my dialog:

1

Using html2canvas, but the image create doesn't show the map:

2

My code:

            function imagem()
            {
                var html2obj = html2canvas($('#dialogPrint'));
                var queue  = html2obj.parse();
                var canvas = html2obj.render(queue);
                var img = canvas.toDataURL();
                window.open(img);
            };

i need help, please.
thanks

@bkralik
Copy link

bkralik commented Mar 6, 2014

Problem is, that google maps uses CSS3 Transformation matrix, which are not fully implemented in html2canvas.

@DanielSBelo
Copy link
Author

how do I do?

@brcontainer
Copy link
Contributor

Google Maps images (images in external server)?

Use proxy: https://github.com/niklasvh/html2canvas#how-does-it-work

@bkralik
Copy link

bkralik commented Mar 6, 2014

brcontainer: I think its illegal (accessing map tiles directly from another computer /proxy/). Only way is to use CORS.

@brcontainer
Copy link
Contributor

@bkralik proxy is not only for you to access "blocked sites", proxy term means something else in case the proxy for html2canvas serve to make the javascript API open images from external servers as if it were on your local site.

The proxy makes a downloaded the external server and html2canvas loads the image only after the download completed.

Read this http://en.wikipedia.org/wiki/Same_origin_policy for you to understand the subject.

@DanielSBelo
Copy link
Author

how do i use this proxy?

@brcontainer
Copy link
Contributor

The link that you have spent all the links to the use of proxy (in php languages​​, C# (asp.net), python and VB (asp classic)).

Maybe you have not noticed the links to the proxies, then I will give you here:
https://github.com/niklasvh/html2canvas/wiki/Proxies

@brcontainer
Copy link
Contributor

When using a new library is always good to read the entire README.

@bkralik
Copy link

bkralik commented Mar 6, 2014

@brcontainer I know what this proxy do - when client want to screenshot page, then SERVER download all of pictures to local folder and then client loads them. But thats wrong - because google doesnt allow direct use of their tilesservers - and from their point of view, somebody is sudenly downloading loads of tiles to server without viewing webpage...

@DanielSBelo
Copy link
Author

i'm using Java.
you have a example to use the proxy?

@bkralik
Copy link

bkralik commented Mar 6, 2014

@brcontainer And as I mentioned, there is really problem with CSS3 transformations, because googlemaps running on Google Chrome use them, so map is not screenable with current implementation. Trust me, I had this problem in project I wrote...

@DanielSBelo
Copy link
Author

@bkralik So, how can i do?
do you have any example?

@brcontainer
Copy link
Contributor

I program in java too (I already created proxies in PHP, C # and VB), but I'm not in time to create a proxy in Java, maybe I can just do it on Sunday.

Its application is JSP or "Java Desktop"?

@DanielSBelo
Copy link
Author

@brcontainer It's a webapplication, using JSF, Primefaces, Javascript and Java

@brcontainer
Copy link
Contributor

@DanielSBelo JSF framework I never used, I program in pure Java, without frameworks, do not know if it will be easy to implement the current code with a code in java part. I wanted to help him, but it really is not timely.

[edited]
About css transforms support, read: https://github.com/niklasvh/html2canvas#contributing

@TGOlson
Copy link

TGOlson commented Aug 10, 2014

@DanielSBelo did you find a good solution for this? I'm having the same problem.

Saving a map as a canvas works fine in Firefox, but fails to save the map in Chrome. I don't think it is directly related to the transformation, but more likely the way Chrome handles CORs. I'm totally stuck trying to find an answer, though.

@bkralik
Copy link

bkralik commented Aug 10, 2014

@TGOlson It's really problem with CSS3 transformations, because current release of html2canvas is able to render only "one level" of transformation - it doesn't stack them.
You can verify that problem is in transformations simply by playing with google maps - usually, screenshot like this is given:
map_2014-08-10_10-44-02
(In css, whole map is positioned correctly but after disabling css3, this happens)
Only solution is to implement whole css3 transformations stack. I don't know, if it's in progress by Niklas, but someone should do it :-)

@alanramires
Copy link

subscribing

@PhaedrusTheGreek
Copy link

I'm also having the same problem - only in Chrome. I'm using html2canvas-proxy-php. Other browsers work fine. Parts of the map are just missing.. seems to be related to resizing the map, adding/removing overlays, etc

@TGOlson
Copy link

TGOlson commented Dec 16, 2014

FYI - if you need to get some map capture functionality up and running quickly, you can always use the google streetview or static maps API. Basically, reconstruct what the current user is looking at on the map (map.getPov, etc.) then get that static image from google.

@PhaedrusTheGreek
Copy link

I don't think that approach works with overlays

@mfirdaus
Copy link

I've just stumbled across this issue. If I'm not mistaken, this stackoverflow question exhibits this problem and I've offered a workaround by reading the css3 transforms and applying them as normal CSS positions.

var transform=$(".gm-style>div:first>div").css("transform")
var comp=transform.split(",") //split up the transform matrix
var mapleft=parseFloat(comp[4]) //get left value
var maptop=parseFloat(comp[5])  //get top value
$(".gm-style>div:first>div").css({ //get the map container. not sure if stable
  "transform":"none",
  "left":mapleft,
  "top":maptop,
})

Perhaps, css3 transforms could be checked and automatically converted to normal CSS positioning while rendering then removing them after render.

@alanramires
Copy link

i'm having an inconstant screen capture function.
Works after a full reload of the page (using CTLR+R on firefox)

here is my code, what basically it does is to generate a 64 base/png image of a captured printsreen of the window and the final result i put into an tag to see if it works.

An here is the function

function ebfPrintScreen(componentName)
{
html2cavnas
([document.body],
{
logging: true,
useCORS: true,
onrendered: function (canvas)
{
img = canvas.toDataURL("image/jpg");

                                                  console.log(img.length);
                                                  console.log(img);

                                                  var imgComp = $c(conponentName);
                                                  imgComp.img.src = img

                                          }
                      }
                );

}

The main objective is to capture the google map route after it is created, but as i say, sometime it works, sometime don't. Any clue on what is going on?

@JordonGreene
Copy link

I am having the same problem. I go to take an image of the map after zooming and panning around and larger portions, to even all of the map, become shrouded in light brown all of a sudden. If anyone has a fix for this in Chrome please let me know.

@cht8687
Copy link

cht8687 commented Dec 7, 2015

tried @mfirdaus 's solution, and it works for noraml map view, however, in streetview, it is still broken...anyone have the same issue?

@gariem
Copy link

gariem commented Jun 6, 2016

After applying @mfirdaus 's solution I was able to get the map view captured. But somehow this code below is making the map unusable (but the html2canvas usable):

$(".gm-style>div:first>div").css({ //get the map container. not sure if stable
      "transform":"none",
      "left":mapleft,
      "top":maptop,
    })

Is there a way to "restore" what that line is doing? For now I'm calling the initMap function again in order to have the map working after calling the html2canvas function witht the transformation code.

@mattweberinactive
Copy link

Does the above script work for Google Maps v3?

My requirement is to take a screenshot of a Google Map v3 with a route drawn on it.

It works nicely in Firefox, but in Chrome there is no marker or route. I am already using custom markers.

I have a difficult time debugging because there is no error in console and logging is so limited.

Has anyone solve the issue in Chrome? I have tried the proxy scripts in two languages, but neither seems to make a difference.

@GCorbel
Copy link

GCorbel commented Nov 22, 2016

I have a similar issue, I copy/cut this code from the internet :

  if($.browser.safari) {// Fix for Chrome
    var transform=$(".gm-style>div:first>div").css("transform");
    var comp=transform.split(","); //split up the transform matrix
    var mapleft=parseFloat(comp[4]); //get left value
    var maptop=parseFloat(comp[5]);  //get top value
    $(".gm-style>div:first>div").css({ //get the map container. not sure if stable
      "transform":"none",
      "left":mapleft,
      "top":maptop,
    });
  }

  html2canvas([$("#map")[0]], {
    logging: false,
    useCORS: true,
    onrendered: function (canvas) {
      $('#screenshot').after(canvas);

      if($.browser.safari) {// Fix for Chrome
        $(".gm-style>div:first>div").css({
          left:0,
          top:0,
          "transform":transform
        });
      }
    }
  });

It works but if I move the map with the handler it doesn't work. I works with markers, polygons, etc. It also works in firefox ( I can move the map ) but not in chrome.

Any idea ?

@niklasvh
Copy link
Owner

Is this still an issue with v1.0.0? If so, could you please share an example on jsfiddle.

@no-response
Copy link

no-response bot commented Dec 12, 2017

This issue has been automatically closed because there has been no response to our request for more information from the original author. With only the information that is currently in the issue, we don't have enough information to take action. Please reach out if you have or find the answers we need so that we can investigate further.

@Ananda-Pryana
Copy link

@niklasvh I can confirm that it is still an issue with the latest version. Here is a fiddle I created while testing this issue: http://jsfiddle.net/9agom947/4/

The fiddle shows the problem as described in the linked stackoverflow question, not necessarily what's in the OP of this thread. If you don't pan the map, there is no problem in getting the map to be copied. Once you pan the map, in Chrome but not in FireFox, the copied map will be blank outside the region that was initially loaded.

image

The fix given in this thread does seem to solve the problem.

@niklasvh niklasvh reopened this Jan 31, 2018
@Juuuke
Copy link

Juuuke commented Feb 14, 2018

@Ananda-Pryana I've tried your jsFiddle however the fix does not seem to work anymore. Is there any other solution ?

Thanks in advance.

@Ananda-Pryana
Copy link

Looks like the latest version of google map (v3.32) released recently has a new experimental renderer.
https://developers.google.com/maps/documentation/javascript/releases

This has broken the fix. I only did a quick testing, but it seems like now things are broken equally in all browsers (not just for Chrome), so hopefully that will make it easier to fix in the next version of html2canvas?

But a quick work-around would be to use the older version of gmap, where the fix would still be working fine.

@Juuuke
Copy link

Juuuke commented Feb 15, 2018

@Ananda-Pryana Yup I downgraded gmap, worked, thanks.

@OneTrueBean
Copy link

Thanks @Ananda-Pryana! I had this working last week then moved it to a new platform and I thought the move was what broke it. I was totally going down a rat hole assuming the new environment was the culprit. I downgraded to 3.30 and all is well.

@rSensation
Copy link

rSensation commented Feb 27, 2018

Seems that in the new version of google maps transform is applied to the different div. Using @GCorbel's solution but with this selector (".gm-style>div:first>div:first>div:last>div") seems to work. Though I haven't yet tested it thoroughly.

@FelipeMicali
Copy link

@rSensation tip worked like a charm in newest version. Thank you!

@OneTrueBean
Copy link

Hmm seems this problem is back for me, I have to pan the map to see the issue and when I pan and use Html2Canvas to get the screen grab, some areas show as blank grey?

@mylesboone
Copy link

mylesboone commented Aug 16, 2018

To any of you dealing with getting overlay layers cut off --
@GCorbel's selector only transforms the google map layer. If you have other overlays, you'll have to find which div they are in (for example, $('.gm-style>div:first>div:first>div:first>div:first>div') was one of my overlay divs and apply the same transform to the css.

@sunghunOW
Copy link

sunghunOW commented Aug 21, 2018

@mylesboone how did you find which div the overlay layers are? I'm currently struggling through the same issue of overlay layers being cut off.

I'm using GmapMarker and GmapPolyline as overlay layers at the moment.

@mylesboone
Copy link

@sunghunOW
A solution can be found here #1568
You can use your browser's inspection tool to see which div's will need transformation.

@hseeda
Copy link

hseeda commented Sep 11, 2018

Best solution I have found:

@hseeda
Copy link

hseeda commented Sep 11, 2018

    html2canvas($('.gm-style>div:eq(0)')[0],{
        useCORS: true,
        allowTaint: true,
        async:false,
    }).then(canvas => {document.body.appendChild(canvas)});

@Jorricks
Copy link

    html2canvas($('.gm-style>div:eq(0)')[0],{
        useCORS: true,
        allowTaint: true,
        async:false,
    }).then(canvas => {document.body.appendChild(canvas)});

This give me canvas is not defined.. Should the selector of the items word out of the box?

@imlinus
Copy link

imlinus commented Mar 6, 2019

@hseeda Thank you! Your selector was doing the trick for me!

Here's my slightly modified selector that does work (at least for me, haha)

const div = document.querySelector('#map > div:first-of-type')

html2canvas(div, {})

However, now it's cutting off the Google Logo which always has to show in order to comply with the terms and conditions :(

Well well, I'll just clone the node or something. I've been fighting this map for a while now :D

@karlazz
Copy link

karlazz commented Apr 9, 2019

This works for me:

$('#snapshot').on('click',function () {
	html2canvas(document.querySelector('.gm-style'), 
           {useCORS:true, allowTaint: true,async:false} ).then(canvas => {
			document.body.appendChild(canvas)
	});
});

@shaji-Dev
Copy link

The issue with blank map or an error generating the canvas was tricky, but eventually what fixed it for me was adding this config:

ignoreElements: (node) => {
        return node.nodeName === 'IFRAME';
      }
html2canvas(mapWrapper, {
      useCORS: true,
      allowTaint: false,
      ignoreElements: (node) => {
        return node.nodeName === 'IFRAME';
      }
    }).then(canvas => {
      const url = canvas.toDataURL('image/png');
      saveAs(url, 'image3.png');
      window.URL.revokeObjectURL(url);
    });

@martin-paliza
Copy link

Thanks to @imlinus and @hseeda ! This selector works perfectly for me! and it even keeps the google logo, thanks!

@hseeda Thank you! Your selector was doing the trick for me!

Here's my slightly modified selector that does work (at least for me, haha)

const div = document.querySelector('#map > div:first-of-type')

html2canvas(div, {})

However, now it's cutting off the Google Logo which always has to show in order to comply with the terms and conditions :(

Well well, I'll just clone the node or something. I've been fighting this map for a while now :D

@id1945
Copy link

id1945 commented Jan 24, 2021

link https://stackblitz.com/edit/angular-agm-maps-html2canvas-nice

And finally it worked with my angular v2 +

import {
  Component,
  OnInit,
  ElementRef,
  ViewChild,
  NgZone
} from "@angular/core";
import { FormControl } from "@angular/forms";
import { MapsAPILoader } from "@agm/core";
declare var google;
import html2canvas from "html2canvas";
import { from } from 'rxjs';

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent implements OnInit {
  public latitude: number;
  public longitude: number;
  public searchControl: FormControl;
  public zoom: number;

  @ViewChild("search", { static: true }) public searchElementRef: ElementRef;
  @ViewChild("screen", { static: true }) screen: ElementRef;
  @ViewChild("canvas", { static: true }) canvas: ElementRef;
  @ViewChild("downloadLink", { static: true }) downloadLink: ElementRef;

  async downloadImage() {
    // Fix issue capture google map
    const mapNode = document.querySelectorAll(".gm-style>div>div>div:last-child>div");
    mapNode.forEach(m => {
      const transformMatrix = getComputedStyle(m).transform.split(",");
      m.setAttribute("style",`left:${parseFloat(transformMatrix[4])}px; top:${parseFloat(transformMatrix[5])}px; transform:none; position:absolute; z-index:9999;`
      );
    });
    const capture$ = from(
      html2canvas(this.screen.nativeElement, { useCORS: true }).then(
        canv => {
          return canv.toDataURL('image/png');
        }, err => {
          throw new Error(err);
        }
      ).catch(res => {
        throw new Error(res);
      })
    );
    // Async
    const image = await capture$.toPromise();
    console.log(image);
    // Loac base64
    this.canvas.nativeElement.src = image;
    // Download image
    this.downloadLink.nativeElement.href = image;
    this.downloadLink.nativeElement.download = "marble-diagram.png";
    this.downloadLink.nativeElement.click();
  }

  constructor(private mapsAPILoader: MapsAPILoader, private ngZone: NgZone) {}

  mapReady(event) {
    console.log(event);
    this.setCurrentPosition();
  }

  ngOnInit() {
    this.zoom = 15;
    this.searchControl = new FormControl();
    this.mapsAPILoader.load().then(() => {
      let autocomplete = new google.maps.places.Autocomplete(
        this.searchElementRef.nativeElement,
        {
          types: ["address"]
        }
      );
      autocomplete.addListener("place_changed", () => {
        this.ngZone.run(() => {
          let place: google.maps.places.PlaceResult = autocomplete.getPlace();
          if (place.geometry === undefined || place.geometry === null) {
            return;
          }
          this.latitude = place.geometry.location.lat();
          this.longitude = place.geometry.location.lng();
          this.zoom = 12;
        });
      });
    });
  }
  recenterMap() {
    this.latitude = 36.8392542;
    this.longitude = 10.313922699999999;
  }

  private setCurrentPosition() {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(position => {
        this.latitude = position.coords.latitude;
        this.longitude = position.coords.longitude;
        this.zoom = 15;
        console.log(this.latitude, this.longitude);
      });
    }
  }

  getPosition() {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(position => {
        this.latitude =
          position.coords.latitude + 0.00000000001 * Math.random();
        this.longitude =
          position.coords.longitude + 0.00000000001 * Math.random();
      });
    } else {
      alert("Geolocation is not supported by this browser.");
    }
  }
}
<button (click)="downloadImage()">Capture</button>

<div class="container" #screen>
	<h2>Angular 2 + Google Maps Places Autocomplete</h2>
	<div class="form-group">
    <input placeholder="search for location" autocorrect="off" autocapitalize="off" spellcheck="off" type="text" class="form-control" #search [formControl]="searchControl">
  </div>
  <agm-map [latitude]="latitude" [longitude]="longitude" [scrollwheel]="false" [zoom]="zoom" [usePanning]='true'
    (mapReady)="mapReady($event)">
    <agm-marker [latitude]="latitude" [longitude]="longitude"
      iconUrl="http://maps.google.com/mapfiles/ms/icons/blue-dot.png"></agm-marker>
  </agm-map>
</div>

<div id="download">
  <img #canvas>
  <a #downloadLink></a>
</div>

@joelgenaro
Copy link

joelgenaro commented Jun 15, 2022

Hello.

I have some problems to capture google map.

I'm using vue-html2canvas and jspdf to capture map, but I can't capture whole map.

   async submitForEstimate() {
    const el = document.getElementById("app");

    // the canvas.
    const options = {
      // type: 'dataURL',
      useCORS: true,
      allowTaint: false,
      ignoreElements: (node) => {
        return node.nodeName === "IFRAME";
      },
    };
    this.output = await this.$html2canvas(el, options);
   }

1

2

I hope for your help.

Thank you.

@sam9116
Copy link

sam9116 commented Feb 18, 2024

I'm leaving this here incase somebody else encounter the same problem
if you are using custom google map styles: make sure to change the map type to raster, otherwise html2canvas will not capture the map
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests