Added UI #4
This commit is contained in:
parent
27866ba7fc
commit
b57d20d055
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"domain": "localhost",
|
"domain": "",
|
||||||
|
"host": "localhost",
|
||||||
"port": 7000,
|
"port": 7000,
|
||||||
"openWebBrowser": true,
|
"openWebBrowser": true,
|
||||||
"clientId": "XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com",
|
"clientId": "XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com",
|
||||||
|
|
83
index.js
83
index.js
|
@ -1,6 +1,7 @@
|
||||||
const config = require('./config.json')
|
const config = require('./config.json')
|
||||||
|
|
||||||
const domain = config.domain;
|
const domain = config.domain;
|
||||||
|
const host = config.host;
|
||||||
const port = config.port;
|
const port = config.port;
|
||||||
const openWebBrowser = config.openWebBrowser; // Set to false if running as a server
|
const openWebBrowser = config.openWebBrowser; // Set to false if running as a server
|
||||||
|
|
||||||
|
@ -12,10 +13,11 @@ const clientSecret = config.clientSecret // Client Secret from Google API page
|
||||||
const open = require('open');
|
const open = require('open');
|
||||||
if (openWebBrowser) {
|
if (openWebBrowser) {
|
||||||
(async () => {
|
(async () => {
|
||||||
await open(`http://${domain}:${port}/`);
|
await open(`http://${host}:${port}/`);
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const favicon = require('serve-favicon');
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const cookieParser = require('cookie-parser')
|
const cookieParser = require('cookie-parser')
|
||||||
|
@ -31,19 +33,42 @@ app.use(
|
||||||
limits: {fileSize: 75 * 1024 * 1024},
|
limits: {fileSize: 75 * 1024 * 1024},
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
// CSS and JS Files
|
||||||
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
|
||||||
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
|
app.use(favicon(__dirname + '/public/assets/icons/favicon.ico'));
|
||||||
|
|
||||||
app.get('/', function (req, res) {
|
app.get('/', function (req, res) {
|
||||||
res.sendFile(__dirname + '/views/index.html')
|
|
||||||
|
res.render('pages/index', {
|
||||||
|
domain: "map.winscloud.net",
|
||||||
|
clientId: clientId
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/upload', function(req, res){
|
app.get('/upload', function(req, res){
|
||||||
res.sendFile(__dirname + '/views/upload.html')
|
res.render('pages/upload');
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/upload', function (req, res) {
|
app.post('/upload', function (req, res) {
|
||||||
|
|
||||||
|
let latitude = req.body["lat"];
|
||||||
|
let longitude = req.body["long"];
|
||||||
|
|
||||||
let key = req.cookies["oauth"]
|
let key = req.cookies["oauth"]
|
||||||
if (!key) {
|
if (!key) {
|
||||||
return res.redirect('/')
|
return res.redirect('/')
|
||||||
} else {
|
} else {
|
||||||
if (!req.files) {
|
if (!req.files) {
|
||||||
return res.status(400).send("Missing file!")
|
return res.status(400).render('pages/error', {
|
||||||
|
errorCode: 400,
|
||||||
|
errorStatus: "Missing File",
|
||||||
|
errorMessage: "Missing File",
|
||||||
|
response: "Error: Missing File"
|
||||||
|
})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Part 1: Get uploadUrl
|
// Part 1: Get uploadUrl
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -56,7 +81,13 @@ app.post('/upload', function (req, res) {
|
||||||
};
|
};
|
||||||
request(options, function (error, response) {
|
request(options, function (error, response) {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error) && res.status(500).send("Error with getting upload url");
|
console.log(error)
|
||||||
|
res.status(500).render('pages/error', {
|
||||||
|
errorCode: 500,
|
||||||
|
errorStatus: "ERROR",
|
||||||
|
errorMessage: "Error: Error with getting upload url",
|
||||||
|
response: JSON.stringify(JSON.parse(response.body), null, 4)
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
let uploadUrl = JSON.parse(response.body)["uploadUrl"]
|
let uploadUrl = JSON.parse(response.body)["uploadUrl"]
|
||||||
// PART 2: Upload the image!
|
// PART 2: Upload the image!
|
||||||
|
@ -70,7 +101,15 @@ app.post('/upload', function (req, res) {
|
||||||
};
|
};
|
||||||
request(options, function (error) {
|
request(options, function (error) {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error) && res.status(500).send("Error with uploading file to uploadUrl");
|
console.log(error)
|
||||||
|
|
||||||
|
res.status(500).render('pages/error', {
|
||||||
|
errorCode: 500,
|
||||||
|
errorStatus: "UPLOAD ERROR",
|
||||||
|
errorMessage: "Error: Error with uploading file to Google's API",
|
||||||
|
response: error
|
||||||
|
})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//PART 3: Set metadata!
|
//PART 3: Set metadata!
|
||||||
let body;
|
let body;
|
||||||
|
@ -81,8 +120,8 @@ app.post('/upload', function (req, res) {
|
||||||
},
|
},
|
||||||
"pose": {
|
"pose": {
|
||||||
"latLngPair": {
|
"latLngPair": {
|
||||||
"latitude": req.body["lat"],
|
"latitude": latitude,
|
||||||
"longitude": req.body["long"]
|
"longitude": longitude
|
||||||
},
|
},
|
||||||
"heading": 0
|
"heading": 0
|
||||||
}
|
}
|
||||||
|
@ -106,12 +145,30 @@ app.post('/upload', function (req, res) {
|
||||||
};
|
};
|
||||||
request(options, function (error, response) {
|
request(options, function (error, response) {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(error) && res.status(500).send("Error with setting metadata of file");
|
console.log(error)
|
||||||
|
|
||||||
|
res.status(500).render('pages/error', {
|
||||||
|
errorCode: 500,
|
||||||
|
errorStatus: "ERROR",
|
||||||
|
errorMessage: "Error with setting metadata of file",
|
||||||
|
response: "Error: Error with setting metadata of file"
|
||||||
|
})
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (JSON.parse(response.body)["error"]) {
|
if (JSON.parse(response.body)["error"]) {
|
||||||
res.status(JSON.parse(response.body)["error"]["code"]).send(`Status: ${JSON.parse(response.body)["error"]["status"]}<br>Error message: ${JSON.parse(response.body)["error"]["message"]}</a><br><a href="/upload">Upload another?</a>`)
|
res.status(500).render('pages/error', {
|
||||||
|
errorCode: JSON.parse(response.body)["error"]["code"],
|
||||||
|
errorStatus: JSON.parse(response.body)["error"]["status"],
|
||||||
|
errorMessage: JSON.parse(response.body)["error"]["message"],
|
||||||
|
response: JSON.stringify(JSON.parse(response.body), null, 4),
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
res.status(200).send(`Status: ${JSON.parse(response.body)["mapsPublishStatus"]}<br>Link: <a href="${JSON.parse(response.body)["shareLink"]}">${JSON.parse(response.body)["shareLink"]}</a><br>You may have to wait awhile after uploading for Google to process the image.<br><a href="/upload">Upload another?</a>`)
|
let shareLink = JSON.parse(response.body)["shareLink"]
|
||||||
|
res.status(200).render('pages/success', {
|
||||||
|
status: JSON.parse(response.body)["mapsPublishStatus"],
|
||||||
|
shareLink: shareLink,
|
||||||
|
response: JSON.stringify(JSON.parse(response.body), null, 4)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -127,7 +184,7 @@ app.get('/auth', function (req, res) {
|
||||||
const request = require('request');
|
const request = require('request');
|
||||||
const options = {
|
const options = {
|
||||||
'method': 'POST',
|
'method': 'POST',
|
||||||
'url': `https://www.googleapis.com/oauth2/v4/token?client_id=${clientId}&client_secret=${clientSecret}&grant_type=authorization_code&code=${req.query["code"]}&redirect_uri=http://${domain}:${port}/auth/&scope=https://www.googleapis.com/auth/streetviewpublish`,
|
'url': `https://www.googleapis.com/oauth2/v4/token?client_id=${clientId}&client_secret=${clientSecret}&grant_type=authorization_code&code=${req.query["code"]}&redirect_uri=https://${domain}/auth/&scope=https://www.googleapis.com/auth/streetviewpublish`,
|
||||||
'headers': {}
|
'headers': {}
|
||||||
};
|
};
|
||||||
request(options, function (error, response) {
|
request(options, function (error, response) {
|
||||||
|
@ -136,7 +193,7 @@ app.get('/auth', function (req, res) {
|
||||||
maxAge: JSON.parse(response.body)["expires_in"] * 1000,
|
maxAge: JSON.parse(response.body)["expires_in"] * 1000,
|
||||||
httpOnly: true
|
httpOnly: true
|
||||||
});
|
});
|
||||||
res.sendFile(__dirname + '/views/upload.html')
|
res.render('pages/upload')
|
||||||
});
|
});
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
|
@ -12,9 +12,11 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"body-parser": "^1.20.1",
|
"body-parser": "^1.20.1",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
|
"ejs": "^3.1.9",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"express-fileupload": "^1.4.0",
|
"express-fileupload": "^1.4.0",
|
||||||
"open": "^8.4.0",
|
"open": "^8.4.0",
|
||||||
"request": "^2.88.2"
|
"request": "^2.88.2",
|
||||||
|
"serve-favicon": "^2.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 72 KiB |
|
@ -0,0 +1,34 @@
|
||||||
|
function readURL(input) {
|
||||||
|
if (input.files && input.files[0]) {
|
||||||
|
|
||||||
|
var reader = new FileReader();
|
||||||
|
|
||||||
|
reader.onload = function(e) {
|
||||||
|
$('.image-upload-wrap').hide();
|
||||||
|
|
||||||
|
$('.file-upload-image').attr('src', e.target.result);
|
||||||
|
$('.file-upload-content').show();
|
||||||
|
|
||||||
|
$('.image-title').html(input.files[0].name);
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(input.files[0]);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
removeUpload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeUpload() {
|
||||||
|
$('.file-upload-input').replaceWith($('.file-upload-input').clone());
|
||||||
|
$('.file-upload-content').hide();
|
||||||
|
$('.image-upload-wrap').show();
|
||||||
|
}
|
||||||
|
|
||||||
|
$('.image-upload-wrap').bind('dragover', function() {
|
||||||
|
$('.image-upload-wrap').addClass('image-dropping');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.image-upload-wrap').bind('dragleave', function() {
|
||||||
|
$('.image-upload-wrap').removeClass('image-dropping');
|
||||||
|
});
|
Binary file not shown.
After Width: | Height: | Size: 125 KiB |
Binary file not shown.
After Width: | Height: | Size: 43 KiB |
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 618 B |
|
@ -0,0 +1,39 @@
|
||||||
|
// Creating map options
|
||||||
|
let mapOptions = {
|
||||||
|
center: [17.385044, 78.486671],
|
||||||
|
zoom: 10,
|
||||||
|
}
|
||||||
|
|
||||||
|
var defaultIcon = L.icon({
|
||||||
|
iconUrl: '/assets/icons/marker-icon.png',
|
||||||
|
shadowUrl: '/assets/icons/marker-shadow.png',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Creating a map object
|
||||||
|
let map = new L.map('map', mapOptions);
|
||||||
|
|
||||||
|
// Creating a Layer object
|
||||||
|
let layer = new L.TileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
});
|
||||||
|
|
||||||
|
// Geolocation
|
||||||
|
L.control.locate().addTo(map);
|
||||||
|
|
||||||
|
// Adding layer to the map
|
||||||
|
map.addLayer(layer);
|
||||||
|
|
||||||
|
// Marker
|
||||||
|
let marker = null;
|
||||||
|
map.on('click', (event)=> {
|
||||||
|
|
||||||
|
if(marker !== null){
|
||||||
|
map.removeLayer(marker);
|
||||||
|
}
|
||||||
|
|
||||||
|
marker = L.marker([event.latlng.lat , event.latlng.lng], {icon: defaultIcon}).addTo(map);
|
||||||
|
|
||||||
|
document.getElementById('lat').value = event.latlng.lat;
|
||||||
|
document.getElementById('long').value = event.latlng.lng;
|
||||||
|
|
||||||
|
})
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,139 @@
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
border-radius: 2px;
|
||||||
|
padding: 12px 18px;
|
||||||
|
font-size: 16px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
cursor: pointer;
|
||||||
|
color: white;
|
||||||
|
background-color: #2196f3;
|
||||||
|
box-shadow: 0 0 4px #999;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
white-space: pre-line;
|
||||||
|
}
|
||||||
|
form, input, label, p {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
#icon {
|
||||||
|
max-width: 45px;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
flex-grow: 1;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
/* Ripple effect */
|
||||||
|
.ripple {
|
||||||
|
background-position: center;
|
||||||
|
transition: background 0.8s;
|
||||||
|
}
|
||||||
|
.ripple:hover {
|
||||||
|
background: #47a7f5 radial-gradient(circle, transparent 1%, #47a7f5 1%) center/15000%;
|
||||||
|
}
|
||||||
|
.ripple:active {
|
||||||
|
background-color: #6eb9f7;
|
||||||
|
background-size: 100%;
|
||||||
|
transition: background 0s;
|
||||||
|
}
|
||||||
|
/* DRAG AND DROP */
|
||||||
|
.file-upload {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.file-upload-btn {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
background: #2196f3;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-bottom: 4px solid #2879f3;
|
||||||
|
transition: all .2s ease;
|
||||||
|
outline: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.file-upload-btn:hover {
|
||||||
|
background: #2175f3;
|
||||||
|
color: #ffffff;
|
||||||
|
transition: all .2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.file-upload-btn:active {
|
||||||
|
border: 0;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
||||||
|
.file-upload-content {
|
||||||
|
display: none;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.file-upload-input {
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
opacity: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.image-upload-wrap {
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 4px dashed #2196f3;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.image-dropping, .image-upload-wrap:hover {
|
||||||
|
background-color: #2196f3;
|
||||||
|
border: 4px dashed #ffffff;
|
||||||
|
}
|
||||||
|
.image-title-wrap {
|
||||||
|
padding: 0 15px 15px 15px;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
.drag-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.drag-text .buttonText {
|
||||||
|
font-weight: 100;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #fff;
|
||||||
|
padding: 60px 0;
|
||||||
|
}
|
||||||
|
.file-upload-image {
|
||||||
|
max-height: 200px;
|
||||||
|
max-width: 200px;
|
||||||
|
margin: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
.remove-image {
|
||||||
|
width: 200px;
|
||||||
|
margin: 0;
|
||||||
|
color: #fff;
|
||||||
|
background: #cd4535;
|
||||||
|
border: none;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
border-bottom: 4px solid #b02818;
|
||||||
|
transition: all .2s ease;
|
||||||
|
outline: none;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.remove-image:hover {
|
||||||
|
background: #c13b2a;
|
||||||
|
color: #ffffff;
|
||||||
|
transition: all .2s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.remove-image:active {
|
||||||
|
border: 0;
|
||||||
|
transition: all .2s ease;
|
||||||
|
}
|
|
@ -1,7 +0,0 @@
|
||||||
<div style="text-align: center;"><h1>PhotoSphereStudio</h1>
|
|
||||||
<p>I am not very good at CSS, so please submit a pull request if you are great at frontend development.</p>
|
|
||||||
<p>Use the button below to authenticate your google account in order to use the service.</p>
|
|
||||||
<a href="https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=XXXXXXXXXXXX-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.apps.googleusercontent.com&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fstreetviewpublish&redirect_uri=http%3A%2F%2Flocalhost%3A7000%2Fauth%2F">
|
|
||||||
<img src="https://developers.google.com/static/identity/images/btn_google_signin_dark_normal_web.png">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
|
||||||
|
<div class="column shadow-sm p-3 m-4 bg-dark-3 text-light">
|
||||||
|
<div align="left">
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<span class="material-symbols-outlined text-danger" style="font-size: 5vw;">
|
||||||
|
close
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<p class="h1 text-danger">
|
||||||
|
<%= errorCode %>: <%= errorStatus %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="h3 text-danger">
|
||||||
|
<%= errorMessage %>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="debugContainer">
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Debug Information</summary>
|
||||||
|
<pre class="text-light"><code><%= response %></code></pre>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uploadButtonContainer">
|
||||||
|
<a href="/upload">
|
||||||
|
<button class="ripple">TRY AGAIN</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include('../partials/footer'); %>
|
|
@ -0,0 +1,13 @@
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
|
||||||
|
<div class="column shadow-sm p-3 m-4 bg-dark-3 text-light">
|
||||||
|
<div align="left">
|
||||||
|
<h1>Get Started</h1>
|
||||||
|
<p>Use the button below to authenticate your google account in order to use the service.</p>
|
||||||
|
<a href="https://accounts.google.com/o/oauth2/v2/auth?response_type=code&client_id=<%= clientId %>&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fstreetviewpublish&redirect_uri=https://<%= domain %>/auth/">
|
||||||
|
<img src="https://developers.google.com/static/identity/images/btn_google_signin_dark_normal_web.png">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include('../partials/footer'); %>
|
|
@ -0,0 +1,44 @@
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
|
||||||
|
<div class="column shadow-sm p-3 m-4 bg-dark-3 text-light">
|
||||||
|
<div align="left">
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<span class="material-symbols-outlined text-success" style="font-size: 5vw;">
|
||||||
|
done
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<p class="h1 text-success">
|
||||||
|
<%= status %>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p class="h3 text-success">
|
||||||
|
Your 360 image has been published to Google Street View! Click on the link below to view the image!
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="viewContainer">
|
||||||
|
<span>Link: </span><a href="<%= shareLink %>"><%= shareLink %></a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<span>You may have to wait awhile after uploading for Google to process the image.</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="debugContainer">
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Debug Information</summary>
|
||||||
|
<pre class="text-light"><code><%= response %></code></pre>
|
||||||
|
</details>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="uploadButtonContainer">
|
||||||
|
<a href="/upload">
|
||||||
|
<button class="ripple">UPLOAD ANOTHER?</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<%- include('../partials/footer'); %>
|
|
@ -0,0 +1,70 @@
|
||||||
|
<%- include('../partials/header'); %>
|
||||||
|
|
||||||
|
<div class="column shadow-sm p-3 m-4 bg-dark-3 text-light">
|
||||||
|
<div align="left">
|
||||||
|
<h1>Upload</h1>
|
||||||
|
<p>This page will allow you to upload photo spheres to Google Maps after the <i>idiots</i> at Google removed their perfectly working StreetView app.</p>
|
||||||
|
|
||||||
|
<div class="alert alert-info" role="alert">
|
||||||
|
<span>Images <b>must</b> have the proper 360 degree file properties, you will need to figure out how to add this metadata. <b>Google Pixel photosphere photos work perfectly</b>, I have yet to figure out how to get my DJI drone 360 photos working.</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
<span>Latitude and Longitude is <b>not required</b> if your photo already contains location data, however if it doesn't, you can use the map below to pinpoint your location</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form name="uploadForm" action="/upload" method="POST" enctype="multipart/form-data">
|
||||||
|
<div class="form-group">
|
||||||
|
<legend>Upload a 360 image</legend>
|
||||||
|
<div class="file-upload">
|
||||||
|
<button class="file-upload-btn" type="button" onclick="$('.file-upload-input').trigger('click')">Add Image</button>
|
||||||
|
|
||||||
|
<div class="image-upload-wrap">
|
||||||
|
<input class="file-upload-input" type='file' id="file" name="file" onchange="readURL(this);" accept="image/png, image/jpeg" />
|
||||||
|
<div class="drag-text">
|
||||||
|
|
||||||
|
<div class="buttonText">
|
||||||
|
|
||||||
|
<span class="material-symbols-outlined" style="font-size: 5vw;">
|
||||||
|
upload
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<h3>Drag and drop a file or select add Image</h3>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="file-upload-content">
|
||||||
|
<img class="file-upload-image" src="" alt="Uploaded Image" />
|
||||||
|
<div class="image-title-wrap">
|
||||||
|
<button type="button" onclick="removeUpload()" class="remove-image">Remove <span class="image-title">Uploaded Image</span></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="map" class="text-light">Map</label>
|
||||||
|
<div id="map" class="leaflet-container" style="max-width: 100%; height: 500px"></div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="lat" class="text-light">Latitude</label>
|
||||||
|
<input type="text" id="lat" name="lat" placeholder="Latitude coordinates" class="form-control" required />
|
||||||
|
</div>
|
||||||
|
<div class="form-control">
|
||||||
|
<label for="long" class="text-light">Longitude</label>
|
||||||
|
<input type="text" id="long" name="long" placeholder="Longitude coordinates" class="form-control" required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="ripple">UPLOAD</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<%- include('../partials/footer'); %>
|
|
@ -0,0 +1,24 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<p class="text-center text-light">Source Code: <a href="https://github.com/moom0o/PhotoSphereStudio/">Github</a> - Made by <a href="https://github.com/moom0o">moom0o</a> & <a href="https://github.com/WinsDominoes">Win</a></p>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/assets/drag-and-drop.js"></script>
|
||||||
|
|
||||||
|
<!-- LEAFLET JS -->
|
||||||
|
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js" integrity="sha256-WBkoXOwTeyKclOHuWtc+i2uENFpDZ9YPdf5Hf+D7ewM=" crossorigin=""></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.js" charset="utf-8"></script>
|
||||||
|
<script src="/assets/leaflet/js/map.js"></script>
|
||||||
|
|
||||||
|
<!-- Optional JavaScript -->
|
||||||
|
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
|
||||||
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
<!-- Then Material JavaScript on top of Bootstrap JavaScript -->
|
||||||
|
<script src="/assets/material/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,42 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
|
||||||
|
<!-- CSS -->
|
||||||
|
<link rel="stylesheet" href="/assets/style.css">
|
||||||
|
|
||||||
|
<!-- Add Material font (Roboto) and Material icon as needed -->
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i,700,700i|Roboto+Mono:300,400,700|Roboto+Slab:300,400,700" rel="stylesheet">
|
||||||
|
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- Add Material CSS, replace Bootstrap CSS -->
|
||||||
|
<link href="/assets/material/material.min.css" rel="stylesheet">
|
||||||
|
|
||||||
|
<!-- GOOGLE ICONS -->
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@48,400,0,0" />
|
||||||
|
|
||||||
|
<!-- LEAFLET FOR MAP -->
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" integrity="sha256-kLaT2GOSpHechhsozzB+flnD+zUyjE2LlfWPgU04xyI=" crossorigin="" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.locatecontrol/dist/L.Control.Locate.min.css" />
|
||||||
|
|
||||||
|
<title>PhotoSphereStudio - Upload 360° Photos to Google Maps</title>
|
||||||
|
</head>
|
||||||
|
<body class="bg-dark-2">
|
||||||
|
<header>
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-info">
|
||||||
|
<a class="navbar-brand" href="/">
|
||||||
|
|
||||||
|
<span>
|
||||||
|
<img src="/assets/icons/icon.png" alt="Icon" id="icon">
|
||||||
|
PhotoSphereStudio
|
||||||
|
</span></a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div align="center">
|
|
@ -1,25 +0,0 @@
|
||||||
<div style="text-align: center;"><h1>PhotoSphereStudio</h1>
|
|
||||||
<p>I am not very good at CSS, so please submit a pull request if you are great at frontend development.</p>
|
|
||||||
<p>This page will allow you to upload photo spheres to Google Maps after the idiots at Google removed their perfectly working StreetView app.</p>
|
|
||||||
<p><a href="https://github.com/moom0o/PhotoSphereStudio">Source Code</a></p>
|
|
||||||
<p>Images must have the proper 360 degree file properties, you will need to figure out how to add this metadata. Google pixel photosphere photos work perfectly, I have yet to figure out how to get my DJI drone 360 photos working.</p>
|
|
||||||
<p>Latitude and Longitude is not required if your photo already contains location data, however if it doesn't, then use <a href="https://www.latlong.net/">https://www.latlong.net/</a> to pinpoint coords.</p>
|
|
||||||
|
|
||||||
<form name="theform" action="/upload" method="POST" enctype="multipart/form-data">
|
|
||||||
<fieldset>
|
|
||||||
<legend>Upload a 360 image</legend>
|
|
||||||
<input type="file"
|
|
||||||
id="file" name="file"
|
|
||||||
accept="image/png, image/jpeg">
|
|
||||||
<div class="form-control">
|
|
||||||
<label for="lat">Latitude</label>
|
|
||||||
<input type="text" id="lat" name="lat" placeholder="Latitude coordinates" />
|
|
||||||
</div>
|
|
||||||
<div class="form-control">
|
|
||||||
<label for="long">Longitude</label>
|
|
||||||
<input type="text" id="long" name="long" placeholder="Longitude coordinates" />
|
|
||||||
</div>
|
|
||||||
<input type="submit" value="Send" class="submit-btn" />
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue