Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reuse a Google Maps street view inside of a modal

I am developing a web app in Rails. When I open a marker, a modal pops up with a street view in a box. I can open one or two markers, but after that I get an error that WebGL has hit a snag. I have tried to look online for resources but nothing makes sense. See below pics for more info. Any help would be greatly appreciated.

First image with error

second image with error

Here is an image of what my console log looks like:

console log

Here is my JavaScript code in my webapp.

<script type="text/javascript">
var handler = Gmaps.build('Google', {
    markers: {
        clusterer: {
            gridSize: 60,
            maxZoom: 20,
            styles: [
                {
                    textSize: 10,
                    textColor: '#ff0000',
                    url: 'assets/creative/m1.png',
                    height: 60,
                    width: 60,
                },
                {
                    textSize: 14,
                    textColor: '#ffff00',
                    url: 'assets/creative/m2.png',
                    height: 60,
                    width: 60,
                },
                {
                    textSize: 18,
                    textColor: '#0000ff',
                    url: 'assets/creative/m3.png',
                    width: 60,
                    height: 60,
                },
            ],
        },
    },
});

var handler2 = Gmaps.build('Google');
var current;
function initialize() {
    handler.buildMap({ internal: {id: 'map'} }, function () {
        markers_json = <%= raw @hash.to_json %>;
        markers = _.map(markers_json, function (marker_json) {
            marker = handler.addMarker(marker_json);
            handler.fitMapToBounds();
            _.extend(marker, marker_json);
            return marker;
        });

        getLocation();

        markers.map(function (elem, index) {
            google.maps.event.addListener(elem.getServiceObject(), 'click', function (evt) {
                var id = elem.id,
                    number = elem.number,
                    name = elem.name,
                    zipcode = elem.zipcode,
                    tabid = elem.tabid,
                    latitude = elem.latitude,
                    longitude = elem.longitude;

                $('.name').html('<h3 class=\'panel-title\'><i class=\'fa fa-id-card\'></i>' + number + '</h3>');
                $('.paneltb').html('<thead><tr><th>Panel</th><th>Location</th><th>Tab ID</th><th>Zip Code</th><th>Latitude</th><th>Longitude</th></tr></thead><tbody><tr><td>' + number + '</td><td>' + name + '</td><td>' + tabid + '</td><td>' + zipcode + '</td><td>' + latitude + '</td><td>' + longitude + '</td></tr></tbody>');

                pos = new google.maps.LatLng(latitude, longitude);

                var div = document.getElementById('map2');
                var sv = new google.maps.StreetViewPanorama(div);
                sv.setPosition(pos);
                sv.setVisible(true);

                // find the heading by looking from the google car pos to the venue pos
                var service = new google.maps.StreetViewService();
                service.getPanoramaByLocation(pos, 50, function (result, status) {
                    if (status == google.maps.StreetViewStatus.OK)
                    {
                        carPos = result.location.latLng;
                        heading = google.maps.geometry.spherical.computeHeading(carPos, pos);
                        sv.setPov({ heading: heading, pitch: 0, zoom: 1 });
                    }
                });

                $('#myModal').modal('show');
                current = elem;
            });
        });
    });
    // Create the search box and link it to the UI element.
}

function getLocation() {
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(displayOnMap);
    } else {
        navigator.geolocation.getCurrentPosition(displayOnMapError);
    }
}

function displayOnMap(position) {
    marker2 = handler.addMarker({
        lat: position.coords.latitude,
        lng: position.coords.longitude,
        picture: {
            url: "<%= asset_path 'creative/1499326997_Untitled-2-01.png' %>",
            width: 48,
            height: 48,
        },
        infowindow: 'You are Here!',
    });
    handler.map.centerOn(marker2);
    handler.getMap().setZoom(10);
}

function displayOnMapError(position) {
    marker2 = handler.addMarker({
        lat: 34.0522,
        lng: -118.2437,
        picture: {
            url: "<%= asset_path 'creative/1499326997_Untitled-2-01.png' %>",
            width: 48,
            height: 48,
        },
    });
    handler.map.centerOn(marker2);
    handler.getMap().setZoom(10);
}

initialize();
</script>

This was the original code to get the modal to pop up with the information I needed.

$(".name").html("<h3 class='panel-title'><i class='fa fa-id-card'></i>"+number+"</h3>");
$(".paneltb").html("<thead><tr><th>Panel</th><th>Location</th><th>Tab ID</th><th>Zip Code</th><th>Latitude</th><th>Longitude</th></tr></thead><tbody><tr><td>"+number+"</td><td>"+ name + "</td><td>"+tabid+"</td><td>"+zipcode+"</td><td>"+latitude+"</td><td>"+longitude+"</td></tr></tbody>");

$('#myModal').modal('show');
current = elem;

In order to add the street view inside of the modal the following code was added:

pos = new google.maps.LatLng( latitude, longitude );

var div = document.getElementById('map2');
var sv = new google.maps.StreetViewPanorama(div);
sv.setPosition(pos);
sv.setVisible(true);

// find the heading by looking from the google car pos to the venue pos
var service = new google.maps.StreetViewService();
service.getPanoramaByLocation(pos, 50, function (result, status) {
    if (status == google.maps.StreetViewStatus.OK) {   
        carPos = result.location.latLng;
        heading = google.maps.geometry.spherical.computeHeading(carPos, pos);
        sv.setPov({ heading: heading, pitch: 0, zoom: 1 });
    }
});

In order to pass the current lat and long into the street view, I had to put it inside of the same function that the modal is being called on.

UPDATE

I cannot figure how to set var sv = new google.maps.StreetViewPanorama(div); so it is set once and the same map reused for each instance of the modal that is called rather than trying to start and restart a new instance.

Update 2

I cannot figure out how to initially initialize this part:

var sv = new google.maps.StreetViewPanorama(div);

So when I open a modal it does not render a new map it just reuses the same map. I am inclined to write another function but I need some guidance, please.

UPDATE 3

Something else I have noticed is that when I click on one marker it will show the street view filling its square in the modal. When I click on another one sometimes it won't show at all but in most cases, it shows but very tiny in the corner as in this image:

update 3

I also noticed that the code for class widget-scene-canvas which comes from Google Maps keeps altering itself from what I originally had it be through my own id styling when I click on more than the first map at separate times.

widget

like image 357
Jermaine Subia Avatar asked Aug 12 '17 19:08

Jermaine Subia


2 Answers

I figured out that I had more then one problem I had three to be exact. Thanks to https://stackoverflow.com/a/19048363/7039895 I was able to figure out that I needed to resize my map inside the modal each time a modal is opened. The script for that which was placed after the modal was opened is:

$("#myModal").on("shown.bs.modal", function () {
    google.maps.event.trigger(sv, "resize");
});  

The second issue occurred when I noticed that some locations when I opened a modal would render the google street maps while others would render a google street map but it was solid black like this image:

error

What I discovered was that I needed to alter the zoom of the rendered street map to 0 because it was at a 1 zoom from the pov of the car and some lats and longs in my map could not zoom while others could and this all depends on where the google car snapped a photo. Here is the code notice where it says zoom I changed that from a 1 zoom to a 0:

var service = new google.maps.StreetViewService();
        service.getPanoramaByLocation( pos, 50, function(result, status) {
            if (status == google.maps.StreetViewStatus.OK) 
            {   
                carPos = result.location.latLng;
                heading = google.maps.geometry.spherical.computeHeading( carPos, pos );
                sv.setPov( { heading: heading, pitch: 0, zoom: 0 } );
            }
        })

The last thing I needed to fix was the webGL hit a snag error which kept popping up. Upon further investigation on my end I noticed whether I was in the web app or just exploring google maps it was popping up regardless. It seemed to be an error with chrome in general so I took a look at this article in particular https://www.xtremerain.com/fix-rats-webgl-hit-snag-chrome/ I followed the first example and disabled (see below) from the advanced settings.

Use hardware acceleration when available

After that I went back and tested the map and each location opens with the google street map showing and the webgl hit a snag error no longer appeared.

Here is the final code for the map portion of my web app:

var handler = Gmaps.build('Google', {
               markers:
                      {clusterer: {
                        gridSize: 60,
                        maxZoom: 20,
                        styles: [ {
                          textSize: 10,
                          textColor: '#ff0000',
                          url: 'assets/creative/m1.png',
                          height: 60,
                          width: 60 }
                        , {
                          textSize: 14, 
                          textColor: '#ffff00',
                          url:'assets/creative/m2.png',
                          height: 60,
                          width: 60 }
                        , {
                         textSize: 18, 
                         textColor: '#0000ff',
                         url: 'assets/creative/m3.png',
                         width: 60,
                         height: 60}
                        ]}}
            });

var current;
function initialize(){
  handler.buildMap({ internal: {id: 'map'} }, function() {

    markers_json = <%=raw @hash.to_json %>;
    markers = _.map(markers_json, function(marker_json){
      marker = handler.addMarker(marker_json);
      handler.fitMapToBounds();
      _.extend(marker, marker_json);
      return marker;
    });

    getLocation();



    markers.map(function(elem, index) {

      google.maps.event.addListener(elem.getServiceObject(), "click", function(evt) {
        var id = elem.id,
            number = elem.number,
            name = elem.name,
            zipcode = elem.zipcode,
            tabid = elem.tabid,
            latitude = elem.latitude,
            longitude = elem.longitude



         $(".name").html("<h3 class='panel-title'><i class='fa fa-id-card'></i>"+number+"</h3>")
         $(".paneltb").html("<thead><tr><th>Panel</th><th>Location</th><th>Tab ID</th><th>Zip Code</th><th>Latitude</th><th>Longitude</th></tr></thead><tbody><tr><td>"+number+"</td><td>"+ name + "</td><td>"+tabid+"</td><td>"+zipcode+"</td><td>"+latitude+"</td><td>"+longitude+"</td></tr></tbody>")


        pos = new google.maps.LatLng( latitude, longitude );
        var div = document.getElementById('map2');
        var sv = new google.maps.StreetViewPanorama(div);



        sv.setPosition( pos );
        sv.setVisible( true );

        // find the heading by looking from the google car pos to the venue pos
        var service = new google.maps.StreetViewService();
        service.getPanoramaByLocation( pos, 50, function(result, status) {
            if (status == google.maps.StreetViewStatus.OK) 
            {   
                carPos = result.location.latLng;
                heading = google.maps.geometry.spherical.computeHeading( carPos, pos );
                sv.setPov( { heading: heading, pitch: 0, zoom: 0 } );
            }
        })

        $('#myModal').modal('show')

            current = elem;

      $("#myModal").on("shown.bs.modal", function () {
    google.maps.event.trigger(sv, "resize");
});  

        });
    })
  });
    // Create the search box and link it to the UI element.


}
function getLocation(){
  if(navigator.geolocation){
    navigator.geolocation.getCurrentPosition(displayOnMap);
  }
  else{
    navigator.geolocation.getCurrentPosition(displayOnMapError);
  }
};
function displayOnMap(position){

  marker2 = handler.addMarker({
    lat: position.coords.latitude,
    lng: position.coords.longitude,
    picture: {
        url: "<%= asset_path 'creative/1499326997_Untitled-2-01.png' %>",
        width:  48,
        height: 48
        },
    infowindow:  "You are Here!"
  });
  handler.map.centerOn(marker2);
  handler.getMap().setZoom(10);
};

function displayOnMapError(position){

  marker2 = handler.addMarker({
    lat: 34.0522,
    lng: -118.2437,
    picture: {
        url: "<%= asset_path 'creative/1499326997_Untitled-2-01.png' %>",
        width:  48,
        height: 48
        }
  });
  handler.map.centerOn(marker2);
  handler.getMap().setZoom(10);
};




initialize();
like image 143
Jermaine Subia Avatar answered Nov 17 '22 03:11

Jermaine Subia


This problem actually, has nothing to do with webGL and JS, (well sort of not), because what I'm guessing is that each time you click on a location, you're initialising a new webGL instance and not reusing your previous map (that was initialised before), which his why you can only "open 1 or 2 before webGL hits a snag".

And this becomes incredibly heavy for the browser, so it decides it's time to go for lunch.

As I'm guessing what you're trying to do is use ajax to render a new map, and what would come in handy then is to be able to destroy the map that was initialised before, but unfortunately, Google maps API do not have a function for destroying a map instance, (Mapbox and other apis do). But google officially explains it as:

Hi everyone,

First, apologies that we've been silent on this issue up to now.

Next, there's an easy work around, which is to reuse your Map instances. If there's a good reason why you can't do this I'd love to know.

I'm labeling this as won't fix, because this is technically difficult. We're not really sure in how many places we're leaking :-( Additionally, we'd probably need to introduce some sort of map.destroy() method. Otherwise, how are we to know whether you're planning to reuse the map? Javascript doesn't have destructors, so we can't know you've thrown away your reference to the object.

Sorry to be the bearer of bad news.

So what you need to make sure of is, that you're not initialising a new map each time you render a new partial. But instead reuse the one you initialised at first.

Your problem lives here:

var sv = new google.maps.StreetViewPanorama(div);

And you're not showing much of how you're calling the pop-up, so I can't really suggest a path for you. Let me know if this makes sense to you.

like image 29
Crashtor Avatar answered Nov 17 '22 03:11

Crashtor