Using Google Street View Photos as Wallpapers [A How-To Guide]

Written by kdelphino | Published 2020/06/22
Tech Story Tags: wallpapers | street-view | python | selenium | google-maps-api | latest-tech-stories | google-street-view-wallpapers | google-street-view-tips

TLDR Google Street View photos are cluttered with interface controls. Google gives free monthly credit to developers to use the Street View API to create clean Street View wallpapers. The Street View Static API has a function to return a photo from a given location. But the maximum size allowed is 640x640. Not ideal for a wallpaper. The API wouldn't help me with that, so I was left with the brute force approach. It's still very much brute force because checking if a given pair of coordinates is valid on Street View is quite difficult.via the TL;DR App

Have you ever seen a photo on Google Street View and thought"Damn, this would make for a nice wallpaper"? Well, I have. So I started a project to get Street View photos at random and turning them into wallpapers.
First problem is that Street View photos are cluttered with interface controls. I soon found out it's a problem that had been already tackled by another kind of people that need clean Street view pictures: journalists. Karl Hodge listed all the elements that should be hidden to get a clean picture. Andy Dickinson went one step further and created a Chrome extension to hide all those elements at once with a single click.
Now, if I only wanted a couple of pictures from Street View, that extension would have sufficed. But I get tired of my wallpapers very quickly. My perfect day starts with a good wallpaper that I have never seen before. I needed a solution that could automatically generate many photos to save me from boredom.
The good news for anyone who wants to automate Google Maps stuff is that there are APIs for that. They aren't free of charge, but Google gives free monthly credit to developers. That was more than enough for my small project.
In my case, the perfect API seemed to be the Street View Static API. It has a function to return a photo from a given location. The call is as simple as this. Problem is that the maximum size allowed is 640x640. Not ideal for a wallpaper.
After some research, I found another API called Maps JavaScript API . It has a Street View Service that allows web developers to include Street View panoramas on their sites. Google even provides a demo that was almost exactly what I was looking for.
Street View Service is flexible enough to allow for the disabling of all interface controls, so no CSS hacking was needed. And as a very welcome bonus, it also permits removing the street names!
Unfortunately, that API doesn't provide a way to get a picture from the panorama the same way the Static API. So the solution I made was to create a simple page with a clean panorama and take a screenshot. Here is the final version of the page's source.
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Clean Street View</title>
    <style>
        /* 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;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        function initPano() {
            // Note: constructed panorama objects have visible: true
            // set by default.
            const urlParams = new URLSearchParams(window.location.search);
            const latitude = parseFloat(urlParams.get('lat'));
            const longitude = parseFloat(urlParams.get('lng'));
            var panorama = new google.maps.StreetViewPanorama(
                document.getElementById('map'), {
                position: { lat: latitude, lng: longitude },
                addressControl: false,
                linksControl: false,
                panControl: false,
                fullscreenControl: false,
                zoomControl: false,
                enableCloseButton: false,
                showRoadLabels: false
            });
        }
    </script>
    <script async defer src="https://maps.googleapis.com/maps/api/js?key=ADD_YOUR_KEY_HERE&callback=initPano"></script>
</body>

</html>
The page gets the query string parameters
lat
and
lng
and displays a full screen clean Street View panorama of the location. This page alone can be used as an alternative to the Clean Street View extension, with the advantage of having the street names removed, but the drawback of needing a Google API key (which, by the way, demands a credit card).
At this point, I needed to somehow save that picture so I could use it later. And here is the part I apologize to all JavaScript developers out there. Although I could create the page (basically because almost all the code was in the Google's demo), I couldn't do the screenshot part. To be fair, I even tried. I resorted to Python.
In Python, one way to render a page that runs some JavaScript is using Selenium. It's a library that allows for task automation on browsers. Originally created for web application testing, Selenium can be used for a variety of tasks, including web scraping, boring stuff automation and taking screenshots from pages :-)
I won't get into the details of how Selenium work. But If you have never used it, it will open a browser and do whatever your code says. In my case, all I needed to do was to open my custom page with some values in the parameters in a fullscreen browser, take a screenshot and save the image file somewhere. Here is a sample code.
import time
from selenium import webdriver

# Fenway Park's coordinates
lat = 42.345573
lng = -71.098326

# Webdriver setup
options = webdriver.chrome.options.Options()
options.add_argument("--log-level=3")  # minimal logging
options.add_argument("--headless")  # hide browser's GUI
options.add_argument("--window-size=1920,1080")
driver = webdriver.Chrome("chromedriver.exe", options=options)

# Get and save image
driver.get(page_path)
time.sleep(1)  # wait for image to load
driver.save_screenshot(str(lat) + "_" + str(lng) + ".png")
Only being able to save a screenshot of a specific location wasn't enough, though. I wanted photos from many random locations. This is another reason I quickly jumped to Python. I wouldn't be able to do that in JavaScript. Sorry again JS folks...
Generating random coordinates that return valid Street View photos is harder than it seems. The API wouldn't help me with that, so I was left with the brute force approach. Fortunately, Google lets you check if a given pair of coordinates has a Street View photo free of charge through the Street View Image Metadata API.
Still, picking random coordinates and checking if they are valid on Street View is quite literally picking drops in the ocean. To alleviate that, I restricted the search of coordinates to be only inside some polygons on Earth's land surface. And what polygons did I use? I drew two big polygons, one around the Americas and the other encompassing Africa, Asia, Europe and Oceania.
It's still very much brute force because I'm not generating the points straight from the polygons, but checking if a totally random coordinate pair is inside one of the polygons. It turns out it can be done with the matplotlib.path.contains_point function, which implements a point-polygon algorithm. And I thought this was a trivial mathematical problem...
Once a point is inside one of the polygons, I proceed to check if the point has a valid Street View panorama. Here is the final Python code:
import sys
import glob
import time
import csv
import requests
import matplotlib.path as mplt_path
from selenium import webdriver
from os import path
from random import uniform

# fmt: off
API_URL = "https://maps.googleapis.com/maps/api/streetview/metadata"  # Not billed
API_KEY = "ADD_YOUR_KEY_HERE"  # get a key at https://developers.google.com/maps/documentation/streetview/get-api-key
WIDTH = 1920
HEIGHT = 1080
# fmt: on

if getattr(sys, "frozen", False):
    script_path = path.dirname(sys.executable)
else:
    script_path = path.dirname(path.abspath(__file__))

# Setup on land polygons
paths = []
for poly_file in glob.glob("polygons\*.csv"):
    with open(poly_file, newline="") as f:
        data = list(csv.reader(f))
        paths.append(mplt_path.Path(data, closed=True))

# Get a valid Street View coordinate
status = False

while status != "OK":
    onland = False

    # Pick a random point that is on land
    while not onland:
        lat, lng = uniform(-180, 180), uniform(-90, 90)

        for p in paths:
            if p.contains_point((lat, lng)):
                onland = True
                break

    # Check if random point has a Street View panorama
    locstring = str(lat) + "," + str(lng)
    r = requests.get(API_URL + "?key=" + API_KEY + "&location=" + locstring)
    status = r.json()["status"]

# Webdriver setup
options = webdriver.chrome.options.Options()
options.add_argument("--log-level=3")  # minimal logging
options.add_argument("--window-size=" + str(WIDTH) + "," + str(HEIGHT))
options.add_argument("--headless")
driver = webdriver.Chrome("chromedriver.exe", options=options)
page_path = path.join(
    script_path, "clean_street_view.html?lat=" + str(lat) + "&lng=" + str(lng)
)
driver.get(page_path)
time.sleep(1)  # wait for image to load
driver.save_screenshot("images\\" + str(lat) + "_" + str(lng) + ".png")
I must say that I'm very satisfied with the results! Most of the time, I get empty roads in the middle of rural areas. These bring me the feel of solitude I was looking for. No more dull wallpapers again.
If want more implementation details, check out the GitHub repository.

Written by kdelphino | Mathematician @ United State
Published by HackerNoon on 2020/06/22