Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to get location change to capture a static image with Google Street View Static API

I am trying to build an application using Ionic2 that allows a user to load a StreetViewPanorama object via the Google Street View Static API. Once the view is loaded, a user should be able to manipulate the street view in any way they choose (move away from the original position, zoom, etc.). Upon completion of this task, a user will capture a static image of the final street view.

My difficulty is arising when I attempt to capture the photo of the new street view location. I am trying to use Google's documentation on static image generation to achieve this. Unfortunately, I am unable to get reference the attributes of a Panorama object after the object is created. I am relatively new to Javascript, so bear with me.

To generate the street view panorama, I run the following functions (starting at the bottom with initMap()):

 /**
  * Creates the map options for panorama generation. This includes adjusting the coordinate
  * position of a user to the nearest available street view. Following creation of the settings,
  * it generates the street view on a user's device.
  *
  * @param userLocation a JSON object whose keys are 'lat' and 'lng' and whose values are
  *                     the corresponding latitude and longitude respectively
  */
  generatePanorama(userLocation): void {
    var streetviewService = new google.maps.StreetViewService;
    streetviewService.getPanorama({
      location: userLocation,
      preference: google.maps.StreetViewPreference.NEAREST,
      radius: 100},
      function(result, status) {
        console.log("Adjusted latitude: ", result.location.latLng.lat(),
                    "\nAdjusted longitude: ", result.location.latLng.lng());
        new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
          position: result.location.latLng,
          pov: {heading: 165, pitch: 0},
          zoom: 1
        });
      });
  }


  /**
  * Uses a device's native geolocation capabilities to get the user's current position
  *
  * @return a JSON object whose keys are 'lat' and 'lng' and whose calues are the corresponding
  *         latitude and longitude respectively
  */
  getLocation(callback): void {
    Geolocation.getCurrentPosition().then((position) => {
      console.log("Latitude: ", position.coords.latitude, "\nLongitude: ", position.coords.longitude);
      callback({lat: position.coords.latitude, lng: position.coords.longitude});
    }).catch((error) => {
      console.log('Error getting location', error);
    });
  }

 /**
  * Initialize a Google Street View Panorama image
  */
  initMap(): void {
    this.getLocation(this.generatePanorama);
  }

I am creating a panorama, as shown above, with the code,

new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
      position: result.location.latLng,
      pov: {heading: 165, pitch: 0},
      zoom: 1
    });

I am unable to assign this object to an instance variable for use in the following two functions:

 /**
  * Generates a URL to query the Google Maps API for a static image of a location
  *
  * @param lat the latitude of the static image to query
  * @param lng the longitude of the static image to query
  * @param heading indicates the compass heading of the camera
  * @param pitch specifies the up or down angle of the camera relative to the street
  * @return a string that is the URL of a statically generated image of a location
  */
  generateStaticMapsURL(lat, lng, heading, pitch): string {
    var url = "https://maps.googleapis.com/maps/api/streetview?size=600x300&location=";
    url += lat + "," + lng;
    url += "&heading=" + heading;
    url += "&pitch=" + pitch;
    url += "&key=SECRET_KEY";
    return url;
  }

  openShareModal() {
    console.log("Final Latitude: ", this.panorama.getPosition().lat());
    console.log("Final Longitude: ", this.panorama.getPosition().lng());
    console.log("Final Heading:", this.panorama.getPov().heading);
    console.log("Final Heading:", this.panorama.getPov().pitch);
    let myModal = this.modalCtrl.create(ShareModalPage);
    myModal.present();
  }

When I try to assign the object to an instance variable either directly or through a helper function I get an UnhandledPromiseRejectionWarning and nothing works. So how exactly can I extract things like location, heading, and pitch from the street view object after it is created?

Thank you for your help!

Update 1: The program is currently building fine. I assign an instance variable of panorama: any; and then proceed to try and update the variable using the following function and assignment.

/**
  * Creates the map options for panorama generation. This includes adjusting the coordinate
  * position of a user to the nearest available street view. Following creation of the settings,
  * it generates the street view on a user's device.
  *
  * @param userLocation a JSON object whose keys are 'lat' and 'lng' and whose values are
  *                     the corresponding latitude and longitude respectively
  */
  generatePanorama(userLocation): void {
    var streetviewService = new google.maps.StreetViewService;
    streetviewService.getPanorama({
      location: userLocation,
      preference: google.maps.StreetViewPreference.NEAREST,
      radius: 100},
      function(result, status) {
        console.log("Adjusted latitude: ", result.location.latLng.lat(),
                    "\nAdjusted longitude: ", result.location.latLng.lng());
        this.panorama = new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
          position: result.location.latLng,
          pov: {heading: 165, pitch: 0},
          zoom: 1
        });
      });
  }

When I do this and then subsequently try to use the panorama variable in another function, it seems to think panorama is an empty variable. Additionally, the panorama map doesn't load at all! Here is the second function I try to use the panorama variable in.

openShareModal() {
    console.log("Final Latitude: ", this.panorama.getPosition().lat());
    console.log("Final Longitude: ", this.panorama.getPosition().lng());
    console.log("Final Heading:", this.panorama.getPov().heading);
    console.log("Final Heading:", this.panorama.getPov().pitch);
    let myModal = this.modalCtrl.create(ShareModalPage);
    myModal.present();
  }

UPDATE 2: Posting the entire chunk of my code for assistance.

import { Component, ViewChild } from '@angular/core';
import { IonicPage, NavController, NavParams, ViewController, ModalController } from 'ionic-angular';
import { ShareModalPage } from '../share-modal/share-modal';
import { Geolocation } from 'ionic-native';
declare var google;

/**
 * Generated class for the StreetViewModalPage page.
 *
 * See https://ionicframework.com/docs/components/#navigation for more info on
 * Ionic pages and navigation.
 */

@IonicPage()
@Component({
  selector: 'page-street-view-modal',
  templateUrl: 'street-view-modal.html',
})

export class StreetViewModalPage {

  @ViewChild('map') mapElement;
  map: any;
  panorama: any;

  constructor(public navCtrl: NavController, public navParams: NavParams,
              public viewCtrl: ViewController, public modalCtrl: ModalController) {}

  ionViewDidLoad() {
    console.log('ionViewDidLoad StreetViewModalPage');
    this.initMap();
  }
  

  /**
  * Creates the map options for panorama generation. This includes adjusting the coordinate
  * position of a user to the nearest available street view. Following creation of the settings,
  * it generates the street view on a user's device.
  *
  * @param userLocation a JSON object whose keys are 'lat' and 'lng' and whose values are
  *                     the corresponding latitude and longitude respectively
  */
  generatePanorama(userLocation): void {
    var streetviewService = new google.maps.StreetViewService;
    streetviewService.getPanorama({
      location: userLocation,
      preference: google.maps.StreetViewPreference.NEAREST,
      radius: 100},
      function(result, status) {
        console.log("Adjusted latitude: ", result.location.latLng.lat(),
                    "\nAdjusted longitude: ", result.location.latLng.lng());
        this.panorama = new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
          position: result.location.latLng,
          pov: {heading: 165, pitch: 0},
          zoom: 1
        });
      });
  }


  /**
  * Uses a device's native geolocation capabilities to get the user's current position
  *
  * @return a JSON object whose keys are 'lat' and 'lng' and whose calues are the corresponding
  *         latitude and longitude respectively
  */
  getLocation(callback): void {
    Geolocation.getCurrentPosition().then((position) => {
      console.log("Latitude: ", position.coords.latitude, "\nLongitude: ", position.coords.longitude);
      callback({lat: position.coords.latitude, lng: position.coords.longitude});
    }).catch((error) => {
      console.log('Error getting location', error);
    });
  }

  /**
  * Initialize a Google Street View Panorama image
  */
  initMap(): void {
    this.getLocation(this.generatePanorama);
  }

  /**
  * Generates a URL to query the Google Maps API for a static image of a location
  *
  * @param lat the latitude of the static image to query
  * @param lng the longitude of the static image to query
  * @param heading indicates the compass heading of the camera
  * @param pitch specifies the up or down angle of the camera relative to the street
  * @return a string that is the URL of a statically generated image of a location
  */
  generateStaticMapsURL(lat, lng, heading, pitch): string {
    var url = "https://maps.googleapis.com/maps/api/streetview?size=600x300&location=";
    url += lat + "," + lng;
    url += "&heading=" + heading;
    url += "&pitch=" + pitch;
    url += "&key=XXXXXXXXXXXX"; // TODO : Make private
    return url;
  }

  openShareModal() {
    console.log("Final Latitude: ", this.panorama.getPosition().lat());
    console.log("Final Longitude: ", this.panorama.getPosition().lng());
    console.log("Final Heading:", this.panorama.getPov().heading);
    console.log("Final Heading:", this.panorama.getPov().pitch);
    let myModal = this.modalCtrl.create(ShareModalPage);
    myModal.present();
  }

}

And the corresponding HTML...

<ion-content>
   <div #map id="street-view" style="height:100%; width:100%;"></div>
   <button ion-button style="position: absolute; top: 5px; right: 5px; z-index: 1;" (click)="openShareModal()" large><ion-icon name="camera"></ion-icon></button>
</ion-content>
like image 454
peachykeen Avatar asked Oct 28 '22 21:10

peachykeen


1 Answers

I think the reason why this.panorama is empty is because of the scope where you are creating it.

I'm on the phone so I can't really type code, but try on the generatePanorama to add const self = this; just at the beginning of the method and when you assign this.panorama please replace with self.panorama = ....

Please let me know if that worked. I'll try it out soon to see if that's all you need.

Here's what I'm talking about

/**
  * Creates the map options for panorama generation. This includes adjusting the coordinate
  * position of a user to the nearest available street view. Following creation of the settings,
  * it generates the street view on a user's device.
  *
  * @param userLocation a JSON object whose keys are 'lat' and 'lng' and whose values are
  *                     the corresponding latitude and longitude respectively
  */
  generatePanorama(userLocation): void {
    var self = this;
    var streetviewService = new google.maps.StreetViewService;
    streetviewService.getPanorama({
      location: userLocation,
      preference: google.maps.StreetViewPreference.NEAREST,
      radius: 100},
      function(result, status) {
        console.log("Adjusted latitude: ", result.location.latLng.lat(),
                    "\nAdjusted longitude: ", result.location.latLng.lng());
        self.panorama = new google.maps.StreetViewPanorama(document.getElementById('street-view'), {
          position: result.location.latLng,
          pov: {heading: 165, pitch: 0},
          zoom: 1
        });
      });
  }

UPDATE - Working Example

class StackTest {
	constructor() {
    this.initMap();
    // This next line is very important to keep the scope.
    this.openShareModal = this.openShareModal.bind(this);
    // This next line should be defined by ionic itself so it won't be needed
    document.getElementById('open').addEventListener('click', this.openShareModal);
  }
  
  generatePanorama(userLocation) {
    var self = this;
    var streetviewService = new google.maps.StreetViewService;
    streetviewService.getPanorama({
      location: userLocation,
      preference: google.maps.StreetViewPreference.NEAREST,
      radius: 100},
      function(result, status) {
        console.log("Adjusted latitude: ", result.location.latLng.lat(),
                    "\nAdjusted longitude: ", result.location.latLng.lng());
        self.panorama = new google.maps.StreetViewPanorama(document.getElementById('pano'), {
          position: result.location.latLng,
          pov: {heading: 165, pitch: 0},
          zoom: 1
        });
        self.bindEvents();
      });
  }
  
  bindEvents() {
  	var self = this;
    this.panorama.addListener('pano_changed', function() {
        var panoCell = document.getElementById('pano-cell');
        panoCell.innerHTML = self.panorama.getPano();
    });

    this.panorama.addListener('links_changed', function() {
        var linksTable = document.getElementById('links_table');
        while (linksTable.hasChildNodes()) {
          linksTable.removeChild(linksTable.lastChild);
        }
        var links = self.panorama.getLinks();
        for (var i in links) {
          var row = document.createElement('tr');
          linksTable.appendChild(row);
          var labelCell = document.createElement('td');
          labelCell.innerHTML = '<b>Link: ' + i + '</b>';
          var valueCell = document.createElement('td');
          valueCell.innerHTML = links[i].description;
          linksTable.appendChild(labelCell);
          linksTable.appendChild(valueCell);
        }
    });

    this.panorama.addListener('position_changed', function() {
        var positionCell = document.getElementById('position-cell');
        positionCell.firstChild.nodeValue = self.panorama.getPosition() + '';
    });

    this.panorama.addListener('pov_changed', function() {
        var headingCell = document.getElementById('heading-cell');
        var pitchCell = document.getElementById('pitch-cell');
        headingCell.firstChild.nodeValue = self.panorama.getPov().heading + '';
        pitchCell.firstChild.nodeValue = self.panorama.getPov().pitch + '';
    });
  }

  getLocation(callback) {
  	callback({lat: 37.869, lng: -122.255});
  }

  initMap() {
    this.getLocation(this.generatePanorama.bind(this));
  }

  openShareModal() {
    console.log("Final Latitude: ", this.panorama.getPosition().lat());
    console.log("Final Longitude: ", this.panorama.getPosition().lng());
    console.log("Final Heading:", this.panorama.getPov().heading);
    console.log("Final Heading:", this.panorama.getPov().pitch);
    alert('If you see this alert this.panorama was defined :)');
    /* let myModal = this.modalCtrl.create(ShareModalPage); */
    /* myModal.present() */;
  }
}

function instantiateTheClass() {
	new StackTest();
}
/* Always set the map height explicitly to define the size of the div
 * element that contains the map. */
#map {
  height: 100%;
}
/* Optional: Makes the sample page fill the window. */
html, body {
  height: 100%;
  margin: 0;
  padding: 0;
}
#open {
  position: absolute;
  top: 0;
  right: 0;
}
#floating-panel {
  position: absolute;
  top: 10px;
  left: 25%;
  z-index: 5;
  background-color: #fff;
  padding: 5px;
  border: 1px solid #999;
  text-align: center;
  font-family: 'Roboto','sans-serif';
  line-height: 30px;
  padding-left: 10px;
}
#pano {
  width: 50%;
  height: 100%;
  float: left;
}
#floating-panel {
  width: 45%;
  height: 100%;
  float: right;
  text-align: left;
  overflow: auto;
  position: static;
  border: 0px solid #999;
}
<div id="pano"></div>
<button id="open">Open modal</button>
<div id="floating-panel">
<table>
  <tr>
    <td><b>Position</b></td><td id="position-cell">&nbsp;</td>
  </tr>
  <tr>
    <td><b>POV Heading</b></td><td id="heading-cell">270</td>
  </tr>
  <tr>
    <td><b>POV Pitch</b></td><td id="pitch-cell">0.0</td>
  </tr>
  <tr>
    <td><b>Pano ID</b></td><td id="pano-cell">&nbsp;</td>
  </tr>
  <table id="links_table"></table>
</table>
</div>
<!-- Replace the value of the key parameter with your own API key. -->
<script async defer
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyAH1G2yf7g4br8sQehvB7C1IfBh8EIaAVE&callback=instantiateTheClass">
</script>
like image 144
Joao Lopes Avatar answered Oct 31 '22 23:10

Joao Lopes