I've been working on this myself. Through trial and error I figured out the following:
@51.508506, -0.125532 -> location
3a -> zoom?
90y -> Field of View
72h -> Heading (measured from North)
90t -> Pitch
data -> unknown
Therefore from this street view you will get this:
https://maps.googleapis.com/maps/api/streetview?size=200x200&location=51.507114,-0.12743&fov=75&heading=329&pitch=1
The big question is what does the data
attribute equate to. Or how we can (if possible) translate it into a pano
attribute (because you cannot get a Photo Sphere image without it)
Edit: I recently asked this question in a similar vein on Stack Overflow and answered my own question.
I think I figured out the pano
attribute.
From said answer:
The data
attribute is the interesting one. This blog article helped me figure it out.
Each segment of the attribute is preceded by "!", a number from 1 - 5 (or more presumably), and a letter (m, e, etc).
For some reason the fourth element (3rd, when counting from 0) is the pano
attribute.
Therefore, this street view of the Acropolis can be parsed into this url for an image:
https://maps.googleapis.com/maps/api/streetview?size=200x200&location=37.971822,23.726532&fov=75&heading=163.13&pitch=7.599999999999994&pano=fPhZjlaq_sAAAAQYNw-Ypw
I'm not sure what the other attributes pertain to, but I'd like to know.
Geometries especially polygons will be are to place in Streetview mode (Earth API would be better).
But Images can be overlayed into streetview
This simple demo shows how to display an image overlay over a streetview and sync its movement (approximately) as the street view POV (point of view) changes
This source is available from 'http://teammaps.com/'
http://projects.teammaps.com/projects/streetviewoverlay/streetviewoverlay.htm
var svo = null;
// the main application object
function SVO()
{
// Trafalgar Square
this.lat = 51.507768;
this.lng = -0.127957;
this.zoom = 16;
this.slat = 51.507527;
this.slng = -0.128652;
this.image = "img/slidecat.gif";
this.pt = new google.maps.LatLng(this.lat, this.lng);
this.streetPt = new google.maps.LatLng(this.slat, this.slng);
// initial POV
this.sheading = 69.58;
this.spitch = 0;
this.szoom = 1;
this.distance = 0; // distance in metres from street view to marker
this.maximumDistance = 200; // distance beyond which marker is hidden
// dimensions of street view container (fixed)
this.panWidth = 480;
this.panHeight = 480;
// dimensions of marker container (resized according to current pov)
this.markerWidth = 120;
this.markerHeight = 80;
}
// create map
SVO.prototype.m_initMap = function ()
{
var mapDiv = eid("mapDiv");
var mapOptions =
{
center: this.pt,
zoom: this.zoom,
mapTypeId: google.maps.MapTypeId.ROADMAP,
scaleControl: true,
mapTypeControl: false
};
map = new google.maps.Map(mapDiv, mapOptions);
}
// create street view
SVO.prototype.m_initPanorama = function ()
{
var visible = false;
var l_panDiv = eid("panDiv");
// controls can be hidden here to prevent the position being changed by the user
var l_panOptions =
{
// zoomControl: false,
// linksControl: false
};
l_panOptions.position = this.streetPt;
l_panOptions.pov =
{
heading: this.sheading,
pitch: this.spitch,
zoom: this.szoom
};
pan = new google.maps.StreetViewPanorama(l_panDiv, l_panOptions);
map.setStreetView(pan);
// event handlers
google.maps.event.addListener(pan, 'pov_changed', function ()
{
svo.m_updateMarker();
});
google.maps.event.addListener(pan, 'zoom_changed', function ()
{
svo.m_updateMarker();
});
google.maps.event.addListener(pan, 'position_changed', function ()
{
svo.streetPt = pan.getPosition();
map.setCenter(svo.streetPt);
svo.m_updateMarker();
});
}
function Marker(p_name, p_icon, p)
{
this.m_icon = "";
this.pt = null;
this.m_pov = null;
this.m_pixelpt = null;
}
// convert the current heading and pitch (degrees) into pixel coordinates
SVO.prototype.m_convertPointProjection = function (p_pov, p_zoom)
{
var l_fovAngleHorizontal = 90 / p_zoom;
var l_fovAngleVertical = 90 / p_zoom;
var l_midX = this.panWidth / 2;
var l_midY = this.panHeight / 2;
var l_diffHeading = this.sheading - p_pov.heading;
l_diffHeading = normalizeAngle(l_diffHeading);
l_diffHeading /= l_fovAngleHorizontal;
var l_diffPitch = (p_pov.pitch - this.spitch) / l_fovAngleVertical;
var x = l_midX + l_diffHeading * this.panWidth;
var y = l_midY + l_diffPitch * this.panHeight;
var l_point = new google.maps.Point(x, y);
return l_point;
}
// create the 'marker' (a div containing an image which can be clicked)
SVO.prototype.m_initMarker = function ()
{
var l_markerDiv = eid("markerDiv");
l_markerDiv.style.width = this.markerWidth + "px";
l_markerDiv.style.height = this.markerHeight + "px";
var l_iconDiv = eid("markerDiv");
l_iconDiv.innerHTML = "<img src='" + this.image + "' width='100%' height='100%' alt='' />";
this.m_updateMarker();
}
SVO.prototype.m_updateMarker = function ()
{
var l_pov = pan.getPov();
if (l_pov)
{
var l_zoom = pan.getZoom();
// scale according to street view zoom level
var l_adjustedZoom = Math.pow(2, l_zoom) / 2;
// recalulate icon heading and pitch now
this.sheading = google.maps.geometry.spherical.computeHeading(this.streetPt, this.pt)
this.distance = google.maps.geometry.spherical.computeDistanceBetween(this.streetPt, this.pt);
var l_pixelPoint = this.m_convertPointProjection(l_pov, l_adjustedZoom);
var l_markerDiv = eid("markerDiv");
var l_distanceScale = 50 / this.distance;
l_adjustedZoom = l_adjustedZoom * l_distanceScale;
// _TODO scale marker according to distance from view point to marker
// beyond maximum range a marker will not be visible
// apply position and size to the marker div
var wd = this.markerWidth * l_adjustedZoom;
var ht = this.markerHeight * l_adjustedZoom;
var x = l_pixelPoint.x - Math.floor(wd / 2);
var y = l_pixelPoint.y - Math.floor(ht / 2);
l_markerDiv.style.display = "block";
l_markerDiv.style.left = x + "px";
l_markerDiv.style.top = y + "px";
l_markerDiv.style.width = wd + "px";
l_markerDiv.style.height = ht + "px";
// hide marker when its beyond the maximum distance
l_markerDiv.style.display = this.distance < this.maximumDistance ? "block" : "none";
// glog("distance = " + Math.floor(this.distance) + " m (" + l_markerDiv.style.display + ") distance scale = " + l_distanceScale + " l_adjustedZoom = " + l_adjustedZoom);
eid("markerInfo").innerHTML = "lat: " + formatFloat(this.streetPt.lat(), 6) + " lng: " + formatFloat(this.streetPt.lng(), 6) + " distance: " + Math.floor(this.distance) + " m";
}
}
// display a message when the user clicks on the marker's div
function markerClick()
{
eid("markerInfo").innerHTML = "<h2>Meow !!!</h2>";
}
function loadPage()
{
svo = new SVO();
svo.m_initMap();
svo.m_initPanorama();
svo.m_initMarker();
}
// utils
function eid(id)
{
return document.getElementById(id);
}
function glog(a)
{
if (typeof (console) != "undefined" && console && console.log)
{
console.log(a);
}
}
function formatFloat(n, d)
{
var m = Math.pow(10, d);
return Math.round(n * m, 10) / m;
}
function normalizeAngle(a)
{
while (a > 180)
{
a -= 360;
}
while (a < -180)
{
a += 360;
}
return a;
}
Best Answer
Well AFAIK there is currently no single Open Source solution for both of the steps to create an free Google Streetview replacement. But IMHO there are a few approaches that try to create some building blocks for crowdsourced/VGI approach on that topic:
Recording
This includes the camera hardware, camera control and panorama stitching:
You might also checkout (near field) photogrammetry, that tries to create 3D representations of single objects or VideoMapping for collecting seemless informations.
Sharing
If a crowd wants to share the results, you need a central platform that supports single steps (requesting, sharing single fotos, importing fotos, stitching panoramas, ...).