├── db └── db ├── public ├── style.css ├── overlay │ ├── sketch.js │ └── index.html ├── logs │ ├── sketch.js │ └── index.html ├── slices │ ├── sketch.js │ └── index.html ├── index.html └── sketch.js ├── .gitignore ├── assets ├── cover.png ├── entry-page.png └── logs-page.png ├── package.json ├── README.md ├── LICENSE └── index.js /db/db: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/style.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | db/* 4 | exports/* -------------------------------------------------------------------------------- /assets/cover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeyklee/data-selfie-app/HEAD/assets/cover.png -------------------------------------------------------------------------------- /assets/entry-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeyklee/data-selfie-app/HEAD/assets/entry-page.png -------------------------------------------------------------------------------- /assets/logs-page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/joeyklee/data-selfie-app/HEAD/assets/logs-page.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "data-selfie-app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js", 9 | "dev": "nodemon index.js" 10 | }, 11 | "author": "", 12 | "license": "ISC", 13 | "dependencies": { 14 | "express": "^4.17.0", 15 | "http": "0.0.0", 16 | "https": "^1.0.0", 17 | "morgan": "^1.9.1", 18 | "nedb": "^1.8.0", 19 | "weather-js": "^2.0.0" 20 | }, 21 | "devDependencies": { 22 | "nodemon": "^1.19.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/overlay/sketch.js: -------------------------------------------------------------------------------- 1 | 2 | let myData; 3 | let img 4 | function preload(){ 5 | myData = loadJSON('/api'); 6 | } 7 | 8 | function setup(){ 9 | // createCanvas(800, 800) 10 | noCanvas(); 11 | noLoop(); 12 | 13 | // colorMode(RGB) 14 | } 15 | 16 | 17 | function draw(){ 18 | 19 | for(let p in myData){ 20 | let item = myData[p]; 21 | 22 | img = createImg(item.image) 23 | // img.hide() 24 | img.size(800, 800) 25 | img.elt.style.position = 'fixed'; 26 | img.position(20,20) 27 | img.elt.style.opacity = "0.5"; 28 | 29 | // const l = ceil(width/Object.keys(myData).length) 30 | // image(img, 0,0, 800, 800) 31 | 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Selfie App 2 | > Here I am is a little web application that allows you to submit an image of your self along with your geolocation. This app is a nice starting point for other self-tracking applications or just a standalone application to create a collection of "dataselfies" with some extra metadata. 3 | 4 | ![](assets/cover.png) 5 | 6 | 7 | ## Setup 8 | 9 | In your terminal: 10 | ```sh 11 | npm install 12 | ``` 13 | 14 | then start your server 15 | ```sh 16 | npm start 17 | ``` 18 | or if in development: `npm run dev` 19 | 20 | Go to: `localhost:3030` to see the magic. 21 | 22 | ## Features (coming soon) 23 | 24 | - [ ] HTTPS support 25 | - [ ] Authentication and login 26 | - [ ] querying other APIs (e.g. weather) 27 | 28 | 29 | ## Gallery 30 | > Prepared for: Quant Humanists 2019 31 | 32 | ![send a photo!](assets/entry-page.png) 33 | ![view your posts](assets/logs-page.png) 34 | 35 | -------------------------------------------------------------------------------- /public/overlay/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Logs 5 | 6 | 7 | 8 |
9 | /📷 10 |
11 |
12 |
13 |
14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2019 joeyklee@nyu.edu 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. -------------------------------------------------------------------------------- /public/logs/sketch.js: -------------------------------------------------------------------------------- 1 | console.log("hello from logs") 2 | 3 | let myData; 4 | let $entries; 5 | function preload(){ 6 | myData = loadJSON('/api'); 7 | } 8 | 9 | function setup(){ 10 | noCanvas(); 11 | noLoop(); 12 | 13 | $entries = select("#entries"); 14 | // console.log(myData); 15 | } 16 | 17 | function myEntryEl(item){ 18 | const myImage = ``; 19 | const lat = nfc(item.location.lat, 4) 20 | const lon = nfc(item.location.lon, 4) 21 | const dateString = moment(item.created).toDate().toString(); 22 | return` 23 |
24 |
${myImage}
25 |
${dateString}
26 |
${lat}, ${lon}
27 |
28 | ` 29 | } 30 | 31 | function draw(){ 32 | 33 | for(p in myData){ 34 | let item = myData[p]; 35 | let itemEl = myEntryEl(item); 36 | // console.log(itemEl) 37 | entries.innerHTML += itemEl 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /public/logs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Logs 5 | 6 | 7 | 8 |
9 | /📷 10 |
11 |
12 |
13 |

My Logs

14 |
15 |
16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/slices/sketch.js: -------------------------------------------------------------------------------- 1 | 2 | let myData; 3 | // let img 4 | function preload(){ 5 | myData = loadJSON('/api'); 6 | } 7 | 8 | function setup(){ 9 | // createCanvas(800, 800) 10 | noCanvas(); 11 | noLoop(); 12 | } 13 | 14 | 15 | function draw(){ 16 | 17 | for(let p in myData){ 18 | let item = myData[p]; 19 | 20 | let img = createImg(item.image).parent('main') 21 | // img.size(400, 400) 22 | // img.elt.style.position = 'fixed'; 23 | const l = ceil(800/Object.keys(myData).length) 24 | img.position(10 + p*l,20) 25 | // img.elt.style.opacity = "0.5"; 26 | img.elt.id = p; 27 | 28 | img.elt.addEventListener('mouseenter', (e) => { 29 | document.querySelectorAll('img').forEach(thing => { 30 | thing.style.zIndex = e.currentTarget.id; 31 | }) 32 | e.currentTarget.style.zIndex = "999999" 33 | }) 34 | 35 | 36 | // tint(255, 100) 37 | 38 | // image(img, p*l,0, 800, 800) 39 | 40 | } 41 | 42 | document.querySelector('body').addEventListener('mouseout', (e) => { 43 | document.querySelectorAll('img').forEach(thing => { 44 | thing.style.zIndex = e.currentTarget.id; 45 | }) 46 | }) 47 | 48 | } -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Data Selfie 5 | 6 | 7 | 8 |
9 | /logs 10 |
11 |
12 |
13 |

Here I am at

14 |

,

15 |
16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /public/slices/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | My Logs 5 | 6 | 33 | 34 | 35 |
36 | /📷 37 |
38 |
39 | 40 |
41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /public/sketch.js: -------------------------------------------------------------------------------- 1 | // console.log("hello from index") 2 | let capture; 3 | let submitButton; 4 | let locationData; 5 | 6 | 7 | function setup(){ 8 | createCanvas(200, 200).parent("#mySketch"); 9 | capture = createCapture(VIDEO); 10 | capture.hide() 11 | capture.size(width, height); 12 | imageMode(CENTER); 13 | getCurrentPosition(doThisOnLocation) 14 | 15 | pixelDensity(0.5); 16 | 17 | submitButton = select("#submitButton"); 18 | submitButton.mousePressed(handleSubmit); 19 | 20 | 21 | 22 | } 23 | 24 | function handleSubmit(e){ 25 | let output = { 26 | location: {}, 27 | image: '' 28 | } 29 | 30 | output.location.lat = locationData.latitude 31 | output.location.lon = locationData.longitude 32 | 33 | const last_img = get() 34 | output.image = last_img.canvas.toDataURL() 35 | 36 | console.log(last_img) 37 | 38 | const options = { 39 | method: 'POST', 40 | headers: { 41 | "Content-Type": "application/json", 42 | // "Content-Type": "application/x-www-form-urlencoded", 43 | }, 44 | body: JSON.stringify(output) 45 | } 46 | fetch(`/api`, options).then(result => { 47 | // updateMyDots() 48 | console.log('success') 49 | }) 50 | 51 | // httpPost("/api", output, (result) =>{ 52 | // // console.log(result); 53 | // console.log("success") 54 | // }); 55 | } 56 | 57 | function doThisOnLocation(position){ 58 | locationData = position 59 | console.log(position.latitude) 60 | console.log(position.longitude) 61 | select("#lat").html( nfc(position.latitude, 4) ) 62 | select("#lon").html( nfc(position.longitude, 4)) 63 | } 64 | 65 | function draw(){ 66 | background(220); 67 | image(capture,width/2,height/2, width*1.3, height) 68 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const logger = require('morgan'); 3 | const path = require('path'); 4 | const http = require('http'); 5 | const fs = require('fs'); 6 | const port = process.env.PORT || 3030; 7 | const Datastore = require('nedb'); 8 | const pathToData = path.resolve(__dirname, "db/db") 9 | const db = new Datastore({ filename: pathToData}); 10 | db.loadDatabase(); 11 | 12 | const app = express() 13 | 14 | 15 | // add logging middleware 16 | app.use(logger("dev")) 17 | 18 | // Handling JSON data 19 | app.use(express.json({limit: '5mb',extended: true})); 20 | app.use(express.urlencoded({limit: '5mb', extended: true})); 21 | 22 | // set the path to the public assets 23 | const publicPath = path.resolve(__dirname, 'public') 24 | app.use( express.static(publicPath)) 25 | 26 | // Show submission page 27 | app.get("/", (req, res) => { 28 | res.sendFile('index.html') 29 | }) 30 | 31 | // Show all my submissions 32 | app.get("/logs", (req, res) => { 33 | res.sendFile('/logs/logs.html') 34 | }) 35 | 36 | // Show all my submissions 37 | // our API 38 | // GET - /api 39 | app.get("/api", (req, res) => { 40 | db.find({}, function (err, docs) { 41 | if(err){ 42 | return err; 43 | } 44 | res.json(docs); 45 | }); 46 | }); 47 | 48 | /** 49 | * 50 | * when we post to the database we will 51 | * query the weatherjs and get our results 52 | * and append them to the incoming geocoordinates 53 | * sent from the client as well as the 54 | * selfie from the webcam. 55 | * */ 56 | app.post("/api", (req, res) => { 57 | // our unix timestamp 58 | const unixTimeCreated = new Date().getTime(); 59 | // add our unix time as a "created" property and add it to our request.body 60 | const newData = Object.assign({"created": unixTimeCreated}, req.body) 61 | 62 | // add in our data object to our database using .insert() 63 | db.insert(newData, (err, docs) =>{ 64 | if(err){ 65 | return err; 66 | } 67 | res.json(docs); 68 | }); 69 | }) 70 | 71 | /** 72 | * 73 | * Export all images to PNG on the server 74 | * */ 75 | app.get("/export/all", (req, res) => { 76 | db.find({}).sort({'created':1}).exec(function (err, docs) { 77 | if(err){ 78 | return err; 79 | } 80 | docs.forEach(item => { 81 | const outImage = item.image.replace(/^data:image\/png;base64,/, ''); 82 | fs.writeFileSync(path.resolve(__dirname, `./exports/all/${item.created}.png`) , outImage, 'base64'); 83 | console.log('writing ', `${item.created}.png`) 84 | }) 85 | res.send('done!') 86 | }); 87 | }) 88 | 89 | 90 | 91 | // use the http module to create an http server listening on the specified port 92 | http.createServer(app).listen(port, () =>{ 93 | console.log(`see the magic at: http://localhost:${port}`) 94 | }) 95 | 96 | --------------------------------------------------------------------------------