From 1121d62808b25c85e063ce0038ab39508420ff3d Mon Sep 17 00:00:00 2001 From: mattdesl Date: Tue, 16 Jun 2015 18:20:02 -0400 Subject: [PATCH] bam! --- .gitignore | 5 ++++ .npmignore | 10 +++++++ LICENSE.md | 21 +++++++++++++++ README.md | 13 +++++++++ demo/index.js | 21 +++++++++++++++ index.js | 17 ++++++++++++ lib/panorama-tiles.js | 51 ++++++++++++++++++++++++++++++++++++ lib/stitch-image-tiles.js | 52 ++++++++++++++++++++++++++++++++++++ package.json | 55 +++++++++++++++++++++++++++++++++++++++ test/image.js | 26 ++++++++++++++++++ 10 files changed, 271 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 LICENSE.md create mode 100644 README.md create mode 100644 demo/index.js create mode 100644 index.js create mode 100644 lib/panorama-tiles.js create mode 100644 lib/stitch-image-tiles.js create mode 100644 package.json create mode 100644 test/image.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9541c46 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +bower_components +node_modules +*.log +.DS_Store +bundle.js diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..13a18f9 --- /dev/null +++ b/.npmignore @@ -0,0 +1,10 @@ +bower_components +node_modules +*.log +.DS_Store +bundle.js +test +test.js +demo/ +.npmignore +LICENSE.md \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..89a78cf --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) +Copyright (c) 2015 Matt DesLauriers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..fdbaaeb --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# google-streetview-equirectangular + +[![stable](http://badges.github.io/stability-badges/dist/stable.svg)](http://github.com/badges/stability-badges) + +Stitches Google Street View tiles into an equirectangular image. + +## Usage + +[![NPM](https://nodei.co/npm/google-streetview-equirectangular.png)](https://www.npmjs.com/package/google-streetview-equirectangular) + +## License + +MIT, see [LICENSE.md](http://github.com/mattdesl/google-streetview-equirectangular/blob/master/LICENSE.md) for details. diff --git a/demo/index.js b/demo/index.js new file mode 100644 index 0000000..c0c0e13 --- /dev/null +++ b/demo/index.js @@ -0,0 +1,21 @@ +var equirect = require('../') +var panorama = require('google-panorama-by-location') + +var loc = [ 51.50700703827454, -0.12791916931155356 ] +panorama(loc, function (err, results) { + if (err) throw err + + var id = results[0].id + var canvas = document.createElement('canvas') + document.body.appendChild(canvas) + canvas.style.width = '100%' + equirect({ + canvas: canvas, + id: id, + zoom: 4 + }).on('complete', function (image) { + console.log('Ready') + }).on('progress', function (ev) { + console.log(ev.count / ev.total) + }) +}) diff --git a/index.js b/index.js new file mode 100644 index 0000000..1b012b7 --- /dev/null +++ b/index.js @@ -0,0 +1,17 @@ +var getTiles = require('./lib/panorama-tiles') +var stitcher = require('./lib/stitch-image-tiles') + +module.exports = equirect + +function equirect (opt, cb) { + opt = opt || {} + var canvas = opt.canvas || document.createElement('canvas') + var context = canvas.getContext('2d') + var data = getTiles(opt) + return stitcher(context, { + tiles: data.tiles, + width: data.width, + height: data.height, + crossOrigin: opt.crossOrigin + }) +} diff --git a/lib/panorama-tiles.js b/lib/panorama-tiles.js new file mode 100644 index 0000000..a96891c --- /dev/null +++ b/lib/panorama-tiles.js @@ -0,0 +1,51 @@ +// Much inspired by: +// https://github.com/spite/PanomNom.js + +var defined = require('defined') + +var widths = [ 416, 832, 1664, 3328, 6656, 13312 ] +var heights = [ 416, 416, 832, 1664, 3328, 6656 ] +var levelsW = [ 1, 2, 4, 7, 13, 26 ] +var levelsH = [ 1, 1, 2, 4, 7, 13 ] + +module.exports = equirect + +function equirect (opt) { + opt = opt || {} + var id = opt.id + if (!opt.id) { + throw new Error('must specify panorama ID') + } + + var zoom = defined(opt.zoom, 1) >>> 0 + var width = widths[zoom] + var height = heights[zoom] + + var cols = levelsW[zoom] + var rows = levelsH[zoom] + var square = 512 + + var tiles = [] + for (var y = 0; y < rows; y++) { + for (var x = 0; x < cols; x++) { + tiles.push({ + url: getURL(id, x, y, zoom), + position: [ x * square, y * square ] + }) + } + } + + return { + columns: cols, + rows: rows, + tileSize: square, + width: width, + height: height, + tiles: tiles + } +} + +function getURL (id, x, y, zoom) { + return 'https://geo0.ggpht.com/cbk?cb_client=maps_sv.tactile&authuser=0&hl=en&panoid=' + id + '&output=tile&x=' + x + '&y=' + y + '&zoom=' + zoom + '&nbt&fover=2' +// return 'https://cbks2.google.com/cbk?cb_client=maps_sv.tactile&authuser=0&hl=en&panoid=' + id + '&output=tile&zoom=' + zoom + '&x=' + x + '&y=' + y + '&' + Date.now() +} diff --git a/lib/stitch-image-tiles.js b/lib/stitch-image-tiles.js new file mode 100644 index 0000000..e7c0a8a --- /dev/null +++ b/lib/stitch-image-tiles.js @@ -0,0 +1,52 @@ +var each = require('async-each') +var loadImage = require('img') +var Emitter = require('events').EventEmitter +var defined = require('defined') + +module.exports = stitch + +function stitch (context, opt) { + if (!context || !context.canvas) { + throw new Error('must provide a canvas rendering context') + } + + opt = opt || {} + + var canvas = context.canvas + var canvasWidth = defined(opt.width, canvas.width) + var canvasHeight = defined(opt.height, canvas.height) + + // failed tiles will show as black + canvas.width = canvasWidth + canvas.height = canvasHeight + context.fillStyle = '#000' + context.fillRect(0, 0, canvasWidth, canvasHeight) + + var emitter = new Emitter() + var count = 0 + var tiles = opt.tiles + var total = tiles.length + var zero = [ 0, 0 ] + + each(tiles, function (item, next) { + loadImage(item.url, opt, function (err, image) { + if (err) { + emitter.emit('not-found', item) + } else { + var position = item.position || zero + context.drawImage(image, position[0] || 0, position[1] || 0) + } + + emitter.emit('progress', { + count: ++count, + total: total + }) + + next(null) + }) + }, function () { + emitter.emit('complete', canvas) + }) + + return emitter +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..286396e --- /dev/null +++ b/package.json @@ -0,0 +1,55 @@ +{ + "name": "google-panorama-equirectangular", + "version": "1.0.0", + "description": "gets equirectangular images from Google StreetView", + "main": "index.js", + "license": "MIT", + "author": { + "name": "Matt DesLauriers", + "email": "dave.des@gmail.com", + "url": "https://github.com/mattdesl" + }, + "dependencies": { + "async-each": "^0.1.6", + "defined": "^1.0.0", + "events": "^1.0.2", + "img": "^1.0.0", + "object-assign": "^3.0.0", + "xhr": "^2.0.2" + }, + "devDependencies": { + "budo": "^4.0.0", + "garnish": "^2.1.3", + "google-panorama-by-location": "^1.0.1", + "is-dom-image": "^1.0.0", + "tap-dev-tool": "^1.3.0", + "tape": "^4.0.0" + }, + "scripts": { + "test": "node test.js", + "start": "budo demo/index.js:bundle.js --live --verbose --dir demo | garnish" + }, + "keywords": [ + "google", + "api", + "street", + "view", + "image", + "equirect", + "equirectangular", + "panorama", + "pano", + "panoramic", + "webgl", + "glsl", + "stackgl" + ], + "repository": { + "type": "git", + "url": "git://github.com/mattdesl/google-panorama-equirectangular.git" + }, + "homepage": "https://github.com/mattdesl/google-panorama-equirectangular", + "bugs": { + "url": "https://github.com/mattdesl/google-panorama-equirectangular/issues" + } +} diff --git a/test/image.js b/test/image.js new file mode 100644 index 0000000..a3a58a6 --- /dev/null +++ b/test/image.js @@ -0,0 +1,26 @@ +/*globals HTMLCanvasElement*/ +var test = require('tape') +var equirect = require('../') + +test('should get image', function (t) { + t.plan(4) + + // can use google-panorama-by-location module + // or StreetViewService#getPanoramaByLocation + var id = 'dXZfBMex9_L7jO2JW3FTdA' + equirect({ + id: id, + crossOrigin: 'Anonymous' + }).on('complete', function (image) { + t.equal(image instanceof HTMLCanvasElement, true, 'canvas') + t.equal(image.width, 832, 'width') + t.equal(image.height, 416, 'height') + + function pixels () { + image.getContext('2d').getImageData(0, 0, 512, 512) + } + t.doesNotThrow(pixels, 'crossOrigin') + }).on('not-found', function (err) { + console.warning(err) + }) +})