From 3b6cd4c2d10e50389278725d530b666dfc0fd8dc Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 14:11:59 -0800 Subject: [PATCH 01/14] initial live earthqukes commit --- .../data_visualization/earthquakes_live.js | 173 +++ examples/data_visualization/testQuakes.json | 153 +++ examples/libraries/tinyColor.js | 1155 +++++++++++++++++ 3 files changed, 1481 insertions(+) create mode 100644 examples/data_visualization/earthquakes_live.js create mode 100644 examples/data_visualization/testQuakes.json create mode 100644 examples/libraries/tinyColor.js diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js new file mode 100644 index 0000000000..76f0fad5b0 --- /dev/null +++ b/examples/data_visualization/earthquakes_live.js @@ -0,0 +1,173 @@ +//earthquakes_live.js +//created by james b. pollack @imgntn on 12/5/2015 +Script.include('../libraries/promise.js'); +var Promise = loadPromise(); + +Script.include('../libraries/tinyColor.js'); +var tinyColor = loadTinyColor(); + +var EARTH_SPHERE_RADIUS = 5; +var EARTH_CENTER_POSITION = Vec3.sum(MyAvatar.position, { + x: 5, + y: 0, + z: 0 +}); + +var EARTH_MODEL_URL='http://public.highfidelity.io/marketplace/hificontent/Scripts/planets/planets/earth.fbx'; +//USGS updates the data every five minutes +var CHECK_QUAKE_FREQUENCY = 300 * 1000; + +var QUAKE_MARKER_DIMENSIONS = { + x: 0.1, + y: 0.1, + z: 0.1 +}; + +function createEarth() { + var earthProperties = { + name: 'Earth', + type: 'Model', + modelURL:EARTH_MODEL_URL, + position: EARTH_CENTER_POSITION, + dimensions: { + x: EARTH_SPHERE_RADIUS, + y: EARTH_SPHERE_RADIUS, + z: EARTH_SPHERE_RADIUS + }, + // color: { + // red: 0, + // green: 100, + // blue: 150 + // }, + collisionsWillMove: false, + userData: JSON.stringify({ + grabbableKey: { + grabbable: false + } + }) + } + + return Entities.addEntity(earthProperties) +} + +function plotLatitudeLongitude(radiusOfSphere, latitude, longitude) { + var tx = radiusOfSphere * Math.cos(latitude) * Math.cos(longitude); + var ty = radiusOfSphere * -Math.sin(latitude); + var tz = radiusOfSphere * Math.cos(latitude) * Math.sin(longitude); + return { + x: tx, + y: ty, + z: tz + } +} + +function getQuakePosition(earthquake) { + var latitude = earthquake.geometry.coordinates[0]; + var longitude = earthquake.geometry.coordinates[1]; + var latlng = plotLatitudeLongitude(2.5, latitude, longitude); + + var position = EARTH_CENTER_POSITION; + var finalPosition = Vec3.sum(position, latlng); + + // print('finalpos::' + JSON.stringify(finalPosition)) + return finalPosition +} + +var QUAKE_URL = 'http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_day.geojson' + +function get(url) { + print('getting' + url) + // Return a new promise. + return new Promise(function(resolve, reject) { + // Do the usual XHR stuff + var req = new XMLHttpRequest(); + req.open('GET', url); + req.onreadystatechange = function() { + print('req status:: ' + JSON.stringify(req.status)) + + if (req.readyState == 4 && req.status == 200) { + var myArr = JSON.parse(req.responseText); + resolve(myArr); + } + }; + + req.send(); + }); +} + +function showEarthquake(snapshot) { + var earthquake = snapshot.val(); + print("Mag " + earthquake.mag + " at " + earthquake.place); +} + +function createQuakeMarker(earthquake) { + var markerProperties = { + name: 'Marker', + type: 'Sphere', + dimensions: QUAKE_MARKER_DIMENSIONS, + position: getQuakePosition(earthquake), + lifetime: 6000, + color: getQuakeMarkerColor(earthquake) + } + + //print('marker properties::' + JSON.stringify(markerProperties)) + return Entities.addEntity(markerProperties); +} + +function getQuakeMarkerColor(earthquake) { + var color = {}; + var magnitude = earthquake.properties.mag; + //realistic but will never get full red coloring and will probably be pretty dull for most. must experiment + var sValue = scale(magnitude, 0, 10, 0, 100); + var HSL_string = "hsl(0, " + sValue + "%, 50%)" + var color = tinyColor(HSL_string); + var finalColor = { + red: color._r, + green: color._g, + blue: color._b + } + + return finalColor +} + +function scale(value, min1, max1, min2, max2) { + return min2 + (max2 - min2) * ((value - min1) / (max1 - min1)); +} + +function processQuakes(earthquakes) { + print('quakers length' + earthquakes.length) + earthquakes.forEach(function(quake) { + // print('PROCESSING A QUAKE') + var marker = createQuakeMarker(quake); + markers.push(marker); + }) + print('markers length:' + markers.length) +} + +var quakea; +var markers = []; + +var earth = createEarth(); + +get(QUAKE_URL).then(function(response) { + print('got it::' + response.features.length) + quakes = response.features; + processQuakes(quakes); + //print("Success!" + JSON.stringify(response)); +}, function(error) { + print('error getting quakes') +}); + +function cleanupMarkers() { + print('CLEANING UP MARKERS') + while (markers.length > 0) { + Entities.deleteEntity(markers.pop()); + } +} + +function cleanupEarth() { + Entities.deleteEntity(earth); +} + +Script.scriptEnding.connect(cleanupMarkers); +Script.scriptEnding.connect(cleanupEarth); \ No newline at end of file diff --git a/examples/data_visualization/testQuakes.json b/examples/data_visualization/testQuakes.json new file mode 100644 index 0000000000..a6429b79c8 --- /dev/null +++ b/examples/data_visualization/testQuakes.json @@ -0,0 +1,153 @@ +{ + "type": "FeatureCollection", + "metadata": { + "generated": 1449347736000, + "url": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson", + "title": "USGS All Earthquakes, Past Hour", + "status": 200, + "api": "1.1.0", + "count": 4 + }, + "features": [{ + "type": "Feature", + "properties": { + "mag": 1.39, + "place": "17km ESE of Julian, California", + "time": 1449345604050, + "updated": 1449345856673, + "tz": -480, + "url": "http://earthquake.usgs.gov/earthquakes/eventpage/ci37498832", + "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci37498832.geojson", + "felt": null, + "cdi": null, + "mmi": null, + "alert": null, + "status": "automatic", + "tsunami": 0, + "sig": 30, + "net": "ci", + "code": "37498832", + "ids": ",ci37498832,", + "sources": ",ci,", + "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", + "nst": 32, + "dmin": 0.1379, + "rms": 0.21, + "gap": 68, + "magType": "ml", + "type": "earthquake", + "title": "M 1.4 - 17km ESE of Julian, California" + }, + "geometry": { + "type": "Point", + "coordinates": [-116.4293333, 33.0301667, 7.84] + }, + "id": "ci37498832" + }, { + "type": "Feature", + "properties": { + "mag": 1.12, + "place": "3km ESE of The Geysers, California", + "time": 1449344686690, + "updated": 1449344783260, + "tz": -480, + "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564866", + "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564866.geojson", + "felt": null, + "cdi": null, + "mmi": null, + "alert": null, + "status": "automatic", + "tsunami": 0, + "sig": 19, + "net": "nc", + "code": "72564866", + "ids": ",nc72564866,", + "sources": ",nc,", + "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", + "nst": 11, + "dmin": 0.01342, + "rms": 0.03, + "gap": 154, + "magType": "md", + "type": "earthquake", + "title": "M 1.1 - 3km ESE of The Geysers, California" + }, + "geometry": { + "type": "Point", + "coordinates": [-122.7176666, 38.7598343, 1.48] + }, + "id": "nc72564866" + }, { + "type": "Feature", + "properties": { + "mag": 1.99, + "place": "3km SE of The Geysers, California", + "time": 1449344287500, + "updated": 1449344383210, + "tz": -480, + "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564856", + "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564856.geojson", + "felt": null, + "cdi": null, + "mmi": null, + "alert": null, + "status": "automatic", + "tsunami": 0, + "sig": 61, + "net": "nc", + "code": "72564856", + "ids": ",nc72564856,", + "sources": ",nc,", + "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", + "nst": 13, + "dmin": 0.01626, + "rms": 0.02, + "gap": 72, + "magType": "md", + "type": "earthquake", + "title": "M 2.0 - 3km SE of The Geysers, California" + }, + "geometry": { + "type": "Point", + "coordinates": [-122.7203369, 38.7571678, 0.3] + }, + "id": "nc72564856" + }, { + "type": "Feature", + "properties": { + "mag": 2.36, + "place": "3km ESE of The Geysers, California", + "time": 1449344196380, + "updated": 1449344459434, + "tz": -480, + "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564836", + "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564836.geojson", + "felt": 0, + "cdi": 1, + "mmi": null, + "alert": null, + "status": "automatic", + "tsunami": 0, + "sig": 86, + "net": "nc", + "code": "72564836", + "ids": ",nc72564836,", + "sources": ",nc,", + "types": ",dyfi,focal-mechanism,general-link,geoserve,nearby-cities,origin,phase-data,scitech-link,", + "nst": 36, + "dmin": 0.01395, + "rms": 0.07, + "gap": 38, + "magType": "md", + "type": "earthquake", + "title": "M 2.4 - 3km ESE of The Geysers, California" + }, + "geometry": { + "type": "Point", + "coordinates": [-122.722168, 38.7598343, 1.21] + }, + "id": "nc72564836" + }], + "bbox": [-122.722168, 33.0301667, 0.3, -116.4293333, 38.7598343, 7.84] +} \ No newline at end of file diff --git a/examples/libraries/tinyColor.js b/examples/libraries/tinyColor.js new file mode 100644 index 0000000000..18aa462010 --- /dev/null +++ b/examples/libraries/tinyColor.js @@ -0,0 +1,1155 @@ +// TinyColor v1.3.0 +// https://github.com/bgrins/TinyColor +// Brian Grinstead, MIT License + + + +var trimLeft = /^\s+/, + trimRight = /\s+$/, + tinyCounter = 0, + math = Math, + mathRound = math.round, + mathMin = math.min, + mathMax = math.max, + mathRandom = math.random; + +function tinycolor (color, opts) { + + color = (color) ? color : ''; + opts = opts || { }; + + // If input is already a tinycolor, return itself + if (color instanceof tinycolor) { + return color; + } + // If we are called as a function, call using new instead + if (!(this instanceof tinycolor)) { + return new tinycolor(color, opts); + } + + var rgb = inputToRGB(color); + this._originalInput = color, + this._r = rgb.r, + this._g = rgb.g, + this._b = rgb.b, + this._a = rgb.a, + this._roundA = mathRound(100*this._a) / 100, + this._format = opts.format || rgb.format; + this._gradientType = opts.gradientType; + + // Don't let the range of [0,255] come back in [0,1]. + // Potentially lose a little bit of precision here, but will fix issues where + // .5 gets interpreted as half of the total, instead of half of 1 + // If it was supposed to be 128, this was already taken care of by `inputToRgb` + if (this._r < 1) { this._r = mathRound(this._r); } + if (this._g < 1) { this._g = mathRound(this._g); } + if (this._b < 1) { this._b = mathRound(this._b); } + + this._ok = rgb.ok; + this._tc_id = tinyCounter++; +} + +tinycolor.prototype = { + isDark: function() { + return this.getBrightness() < 128; + }, + isLight: function() { + return !this.isDark(); + }, + isValid: function() { + return this._ok; + }, + getOriginalInput: function() { + return this._originalInput; + }, + getFormat: function() { + return this._format; + }, + getAlpha: function() { + return this._a; + }, + getBrightness: function() { + //http://www.w3.org/TR/AERT#color-contrast + var rgb = this.toRgb(); + return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000; + }, + getLuminance: function() { + //http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef + var rgb = this.toRgb(); + var RsRGB, GsRGB, BsRGB, R, G, B; + RsRGB = rgb.r/255; + GsRGB = rgb.g/255; + BsRGB = rgb.b/255; + + if (RsRGB <= 0.03928) {R = RsRGB / 12.92;} else {R = Math.pow(((RsRGB + 0.055) / 1.055), 2.4);} + if (GsRGB <= 0.03928) {G = GsRGB / 12.92;} else {G = Math.pow(((GsRGB + 0.055) / 1.055), 2.4);} + if (BsRGB <= 0.03928) {B = BsRGB / 12.92;} else {B = Math.pow(((BsRGB + 0.055) / 1.055), 2.4);} + return (0.2126 * R) + (0.7152 * G) + (0.0722 * B); + }, + setAlpha: function(value) { + this._a = boundAlpha(value); + this._roundA = mathRound(100*this._a) / 100; + return this; + }, + toHsv: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: this._a }; + }, + toHsvString: function() { + var hsv = rgbToHsv(this._r, this._g, this._b); + var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100); + return (this._a == 1) ? + "hsv(" + h + ", " + s + "%, " + v + "%)" : + "hsva(" + h + ", " + s + "%, " + v + "%, "+ this._roundA + ")"; + }, + toHsl: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: this._a }; + }, + toHslString: function() { + var hsl = rgbToHsl(this._r, this._g, this._b); + var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100); + return (this._a == 1) ? + "hsl(" + h + ", " + s + "%, " + l + "%)" : + "hsla(" + h + ", " + s + "%, " + l + "%, "+ this._roundA + ")"; + }, + toHex: function(allow3Char) { + return rgbToHex(this._r, this._g, this._b, allow3Char); + }, + toHexString: function(allow3Char) { + return '#' + this.toHex(allow3Char); + }, + toHex8: function() { + return rgbaToHex(this._r, this._g, this._b, this._a); + }, + toHex8String: function() { + return '#' + this.toHex8(); + }, + toRgb: function() { + return { r: mathRound(this._r), g: mathRound(this._g), b: mathRound(this._b), a: this._a }; + }, + toRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ")" : + "rgba(" + mathRound(this._r) + ", " + mathRound(this._g) + ", " + mathRound(this._b) + ", " + this._roundA + ")"; + }, + toPercentageRgb: function() { + return { r: mathRound(bound01(this._r, 255) * 100) + "%", g: mathRound(bound01(this._g, 255) * 100) + "%", b: mathRound(bound01(this._b, 255) * 100) + "%", a: this._a }; + }, + toPercentageRgbString: function() { + return (this._a == 1) ? + "rgb(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%)" : + "rgba(" + mathRound(bound01(this._r, 255) * 100) + "%, " + mathRound(bound01(this._g, 255) * 100) + "%, " + mathRound(bound01(this._b, 255) * 100) + "%, " + this._roundA + ")"; + }, + toName: function() { + if (this._a === 0) { + return "transparent"; + } + + if (this._a < 1) { + return false; + } + + return hexNames[rgbToHex(this._r, this._g, this._b, true)] || false; + }, + toFilter: function(secondColor) { + var hex8String = '#' + rgbaToHex(this._r, this._g, this._b, this._a); + var secondHex8String = hex8String; + var gradientType = this._gradientType ? "GradientType = 1, " : ""; + + if (secondColor) { + var s = tinycolor(secondColor); + secondHex8String = s.toHex8String(); + } + + return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr="+hex8String+",endColorstr="+secondHex8String+")"; + }, + toString: function(format) { + var formatSet = !!format; + format = format || this._format; + + var formattedString = false; + var hasAlpha = this._a < 1 && this._a >= 0; + var needsAlphaFormat = !formatSet && hasAlpha && (format === "hex" || format === "hex6" || format === "hex3" || format === "name"); + + if (needsAlphaFormat) { + // Special case for "transparent", all other non-alpha formats + // will return rgba when there is transparency. + if (format === "name" && this._a === 0) { + return this.toName(); + } + return this.toRgbString(); + } + if (format === "rgb") { + formattedString = this.toRgbString(); + } + if (format === "prgb") { + formattedString = this.toPercentageRgbString(); + } + if (format === "hex" || format === "hex6") { + formattedString = this.toHexString(); + } + if (format === "hex3") { + formattedString = this.toHexString(true); + } + if (format === "hex8") { + formattedString = this.toHex8String(); + } + if (format === "name") { + formattedString = this.toName(); + } + if (format === "hsl") { + formattedString = this.toHslString(); + } + if (format === "hsv") { + formattedString = this.toHsvString(); + } + + return formattedString || this.toHexString(); + }, + clone: function() { + return tinycolor(this.toString()); + }, + + _applyModification: function(fn, args) { + var color = fn.apply(null, [this].concat([].slice.call(args))); + this._r = color._r; + this._g = color._g; + this._b = color._b; + this.setAlpha(color._a); + return this; + }, + lighten: function() { + return this._applyModification(lighten, arguments); + }, + brighten: function() { + return this._applyModification(brighten, arguments); + }, + darken: function() { + return this._applyModification(darken, arguments); + }, + desaturate: function() { + return this._applyModification(desaturate, arguments); + }, + saturate: function() { + return this._applyModification(saturate, arguments); + }, + greyscale: function() { + return this._applyModification(greyscale, arguments); + }, + spin: function() { + return this._applyModification(spin, arguments); + }, + + _applyCombination: function(fn, args) { + return fn.apply(null, [this].concat([].slice.call(args))); + }, + analogous: function() { + return this._applyCombination(analogous, arguments); + }, + complement: function() { + return this._applyCombination(complement, arguments); + }, + monochromatic: function() { + return this._applyCombination(monochromatic, arguments); + }, + splitcomplement: function() { + return this._applyCombination(splitcomplement, arguments); + }, + triad: function() { + return this._applyCombination(triad, arguments); + }, + tetrad: function() { + return this._applyCombination(tetrad, arguments); + } +}; + +// If input is an object, force 1 into "1.0" to handle ratios properly +// String input requires "1.0" as input, so 1 will be treated as 1 +tinycolor.fromRatio = function(color, opts) { + if (typeof color == "object") { + var newColor = {}; + for (var i in color) { + if (color.hasOwnProperty(i)) { + if (i === "a") { + newColor[i] = color[i]; + } + else { + newColor[i] = convertToPercentage(color[i]); + } + } + } + color = newColor; + } + + return tinycolor(color, opts); +}; + +// Given a string or object, convert that input to RGB +// Possible string inputs: +// +// "red" +// "#f00" or "f00" +// "#ff0000" or "ff0000" +// "#ff000000" or "ff000000" +// "rgb 255 0 0" or "rgb (255, 0, 0)" +// "rgb 1.0 0 0" or "rgb (1, 0, 0)" +// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1" +// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1" +// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%" +// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1" +// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%" +// +function inputToRGB(color) { + + var rgb = { r: 0, g: 0, b: 0 }; + var a = 1; + var ok = false; + var format = false; + + if (typeof color == "string") { + color = stringInputToObject(color); + } + + if (typeof color == "object") { + if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) { + rgb = rgbToRgb(color.r, color.g, color.b); + ok = true; + format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) { + color.s = convertToPercentage(color.s); + color.v = convertToPercentage(color.v); + rgb = hsvToRgb(color.h, color.s, color.v); + ok = true; + format = "hsv"; + } + else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) { + color.s = convertToPercentage(color.s); + color.l = convertToPercentage(color.l); + rgb = hslToRgb(color.h, color.s, color.l); + ok = true; + format = "hsl"; + } + + if (color.hasOwnProperty("a")) { + a = color.a; + } + } + + a = boundAlpha(a); + + return { + ok: ok, + format: color.format || format, + r: mathMin(255, mathMax(rgb.r, 0)), + g: mathMin(255, mathMax(rgb.g, 0)), + b: mathMin(255, mathMax(rgb.b, 0)), + a: a + }; +} + + +// Conversion Functions +// -------------------- + +// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from: +// + +// `rgbToRgb` +// Handle bounds / percentage checking to conform to CSS color spec +// +// *Assumes:* r, g, b in [0, 255] or [0, 1] +// *Returns:* { r, g, b } in [0, 255] +function rgbToRgb(r, g, b){ + return { + r: bound01(r, 255) * 255, + g: bound01(g, 255) * 255, + b: bound01(b, 255) * 255 + }; +} + +// `rgbToHsl` +// Converts an RGB color value to HSL. +// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1] +// *Returns:* { h, s, l } in [0,1] +function rgbToHsl(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, l = (max + min) / 2; + + if(max == min) { + h = s = 0; // achromatic + } + else { + var d = max - min; + s = l > 0.5 ? d / (2 - max - min) : d / (max + min); + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + + h /= 6; + } + + return { h: h, s: s, l: l }; +} + +// `hslToRgb` +// Converts an HSL color value to RGB. +// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100] +// *Returns:* { r, g, b } in the set [0, 255] +function hslToRgb(h, s, l) { + var r, g, b; + + h = bound01(h, 360); + s = bound01(s, 100); + l = bound01(l, 100); + + function hue2rgb(p, q, t) { + if(t < 0) t += 1; + if(t > 1) t -= 1; + if(t < 1/6) return p + (q - p) * 6 * t; + if(t < 1/2) return q; + if(t < 2/3) return p + (q - p) * (2/3 - t) * 6; + return p; + } + + if(s === 0) { + r = g = b = l; // achromatic + } + else { + var q = l < 0.5 ? l * (1 + s) : l + s - l * s; + var p = 2 * l - q; + r = hue2rgb(p, q, h + 1/3); + g = hue2rgb(p, q, h); + b = hue2rgb(p, q, h - 1/3); + } + + return { r: r * 255, g: g * 255, b: b * 255 }; +} + +// `rgbToHsv` +// Converts an RGB color value to HSV +// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1] +// *Returns:* { h, s, v } in [0,1] +function rgbToHsv(r, g, b) { + + r = bound01(r, 255); + g = bound01(g, 255); + b = bound01(b, 255); + + var max = mathMax(r, g, b), min = mathMin(r, g, b); + var h, s, v = max; + + var d = max - min; + s = max === 0 ? 0 : d / max; + + if(max == min) { + h = 0; // achromatic + } + else { + switch(max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return { h: h, s: s, v: v }; +} + +// `hsvToRgb` +// Converts an HSV color value to RGB. +// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100] +// *Returns:* { r, g, b } in the set [0, 255] + function hsvToRgb(h, s, v) { + + h = bound01(h, 360) * 6; + s = bound01(s, 100); + v = bound01(v, 100); + + var i = math.floor(h), + f = h - i, + p = v * (1 - s), + q = v * (1 - f * s), + t = v * (1 - (1 - f) * s), + mod = i % 6, + r = [v, q, p, p, t, v][mod], + g = [t, v, v, q, p, p][mod], + b = [p, p, t, v, v, q][mod]; + + return { r: r * 255, g: g * 255, b: b * 255 }; +} + +// `rgbToHex` +// Converts an RGB color to hex +// Assumes r, g, and b are contained in the set [0, 255] +// Returns a 3 or 6 character hex +function rgbToHex(r, g, b, allow3Char) { + + var hex = [ + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + // Return a 3 character hex if possible + if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) { + return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0); + } + + return hex.join(""); +} + +// `rgbaToHex` +// Converts an RGBA color plus alpha transparency to hex +// Assumes r, g, b and a are contained in the set [0, 255] +// Returns an 8 character hex +function rgbaToHex(r, g, b, a) { + + var hex = [ + pad2(convertDecimalToHex(a)), + pad2(mathRound(r).toString(16)), + pad2(mathRound(g).toString(16)), + pad2(mathRound(b).toString(16)) + ]; + + return hex.join(""); +} + +// `equals` +// Can be called with any tinycolor input +tinycolor.equals = function (color1, color2) { + if (!color1 || !color2) { return false; } + return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString(); +}; + +tinycolor.random = function() { + return tinycolor.fromRatio({ + r: mathRandom(), + g: mathRandom(), + b: mathRandom() + }); +}; + + +// Modification Functions +// ---------------------- +// Thanks to less.js for some of the basics here +// + +function desaturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s -= amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); +} + +function saturate(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.s += amount / 100; + hsl.s = clamp01(hsl.s); + return tinycolor(hsl); +} + +function greyscale(color) { + return tinycolor(color).desaturate(100); +} + +function lighten (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l += amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); +} + +function brighten(color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var rgb = tinycolor(color).toRgb(); + rgb.r = mathMax(0, mathMin(255, rgb.r - mathRound(255 * - (amount / 100)))); + rgb.g = mathMax(0, mathMin(255, rgb.g - mathRound(255 * - (amount / 100)))); + rgb.b = mathMax(0, mathMin(255, rgb.b - mathRound(255 * - (amount / 100)))); + return tinycolor(rgb); +} + +function darken (color, amount) { + amount = (amount === 0) ? 0 : (amount || 10); + var hsl = tinycolor(color).toHsl(); + hsl.l -= amount / 100; + hsl.l = clamp01(hsl.l); + return tinycolor(hsl); +} + +// Spin takes a positive or negative amount within [-360, 360] indicating the change of hue. +// Values outside of this range will be wrapped into this range. +function spin(color, amount) { + var hsl = tinycolor(color).toHsl(); + var hue = (hsl.h + amount) % 360; + hsl.h = hue < 0 ? 360 + hue : hue; + return tinycolor(hsl); +} + +// Combination Functions +// --------------------- +// Thanks to jQuery xColor for some of the ideas behind these +// + +function complement(color) { + var hsl = tinycolor(color).toHsl(); + hsl.h = (hsl.h + 180) % 360; + return tinycolor(hsl); +} + +function triad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l }) + ]; +} + +function tetrad(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }), + tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l }) + ]; +} + +function splitcomplement(color) { + var hsl = tinycolor(color).toHsl(); + var h = hsl.h; + return [ + tinycolor(color), + tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}), + tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l}) + ]; +} + +function analogous(color, results, slices) { + results = results || 6; + slices = slices || 30; + + var hsl = tinycolor(color).toHsl(); + var part = 360 / slices; + var ret = [tinycolor(color)]; + + for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) { + hsl.h = (hsl.h + part) % 360; + ret.push(tinycolor(hsl)); + } + return ret; +} + +function monochromatic(color, results) { + results = results || 6; + var hsv = tinycolor(color).toHsv(); + var h = hsv.h, s = hsv.s, v = hsv.v; + var ret = []; + var modification = 1 / results; + + while (results--) { + ret.push(tinycolor({ h: h, s: s, v: v})); + v = (v + modification) % 1; + } + + return ret; +} + +// Utility Functions +// --------------------- + +tinycolor.mix = function(color1, color2, amount) { + amount = (amount === 0) ? 0 : (amount || 50); + + var rgb1 = tinycolor(color1).toRgb(); + var rgb2 = tinycolor(color2).toRgb(); + + var p = amount / 100; + var w = p * 2 - 1; + var a = rgb2.a - rgb1.a; + + var w1; + + if (w * a == -1) { + w1 = w; + } else { + w1 = (w + a) / (1 + w * a); + } + + w1 = (w1 + 1) / 2; + + var w2 = 1 - w1; + + var rgba = { + r: rgb2.r * w1 + rgb1.r * w2, + g: rgb2.g * w1 + rgb1.g * w2, + b: rgb2.b * w1 + rgb1.b * w2, + a: rgb2.a * p + rgb1.a * (1 - p) + }; + + return tinycolor(rgba); +}; + + +// Readability Functions +// --------------------- +// false +// tinycolor.isReadable("#000", "#111",{level:"AA",size:"large"}) => false +tinycolor.isReadable = function(color1, color2, wcag2) { + var readability = tinycolor.readability(color1, color2); + var wcag2Parms, out; + + out = false; + + wcag2Parms = validateWCAG2Parms(wcag2); + switch (wcag2Parms.level + wcag2Parms.size) { + case "AAsmall": + case "AAAlarge": + out = readability >= 4.5; + break; + case "AAlarge": + out = readability >= 3; + break; + case "AAAsmall": + out = readability >= 7; + break; + } + return out; + +}; + +// `mostReadable` +// Given a base color and a list of possible foreground or background +// colors for that base, returns the most readable color. +// Optionally returns Black or White if the most readable color is unreadable. +// *Example* +// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:false}).toHexString(); // "#112255" +// tinycolor.mostReadable(tinycolor.mostReadable("#123", ["#124", "#125"],{includeFallbackColors:true}).toHexString(); // "#ffffff" +// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"large"}).toHexString(); // "#faf3f3" +// tinycolor.mostReadable("#a8015a", ["#faf3f3"],{includeFallbackColors:true,level:"AAA",size:"small"}).toHexString(); // "#ffffff" +tinycolor.mostReadable = function(baseColor, colorList, args) { + var bestColor = null; + var bestScore = 0; + var readability; + var includeFallbackColors, level, size ; + args = args || {}; + includeFallbackColors = args.includeFallbackColors ; + level = args.level; + size = args.size; + + for (var i= 0; i < colorList.length ; i++) { + readability = tinycolor.readability(baseColor, colorList[i]); + if (readability > bestScore) { + bestScore = readability; + bestColor = tinycolor(colorList[i]); + } + } + + if (tinycolor.isReadable(baseColor, bestColor, {"level":level,"size":size}) || !includeFallbackColors) { + return bestColor; + } + else { + args.includeFallbackColors=false; + return tinycolor.mostReadable(baseColor,["#fff", "#000"],args); + } +}; + + +// Big List of Colors +// ------------------ +// +var names = tinycolor.names = { + aliceblue: "f0f8ff", + antiquewhite: "faebd7", + aqua: "0ff", + aquamarine: "7fffd4", + azure: "f0ffff", + beige: "f5f5dc", + bisque: "ffe4c4", + black: "000", + blanchedalmond: "ffebcd", + blue: "00f", + blueviolet: "8a2be2", + brown: "a52a2a", + burlywood: "deb887", + burntsienna: "ea7e5d", + cadetblue: "5f9ea0", + chartreuse: "7fff00", + chocolate: "d2691e", + coral: "ff7f50", + cornflowerblue: "6495ed", + cornsilk: "fff8dc", + crimson: "dc143c", + cyan: "0ff", + darkblue: "00008b", + darkcyan: "008b8b", + darkgoldenrod: "b8860b", + darkgray: "a9a9a9", + darkgreen: "006400", + darkgrey: "a9a9a9", + darkkhaki: "bdb76b", + darkmagenta: "8b008b", + darkolivegreen: "556b2f", + darkorange: "ff8c00", + darkorchid: "9932cc", + darkred: "8b0000", + darksalmon: "e9967a", + darkseagreen: "8fbc8f", + darkslateblue: "483d8b", + darkslategray: "2f4f4f", + darkslategrey: "2f4f4f", + darkturquoise: "00ced1", + darkviolet: "9400d3", + deeppink: "ff1493", + deepskyblue: "00bfff", + dimgray: "696969", + dimgrey: "696969", + dodgerblue: "1e90ff", + firebrick: "b22222", + floralwhite: "fffaf0", + forestgreen: "228b22", + fuchsia: "f0f", + gainsboro: "dcdcdc", + ghostwhite: "f8f8ff", + gold: "ffd700", + goldenrod: "daa520", + gray: "808080", + green: "008000", + greenyellow: "adff2f", + grey: "808080", + honeydew: "f0fff0", + hotpink: "ff69b4", + indianred: "cd5c5c", + indigo: "4b0082", + ivory: "fffff0", + khaki: "f0e68c", + lavender: "e6e6fa", + lavenderblush: "fff0f5", + lawngreen: "7cfc00", + lemonchiffon: "fffacd", + lightblue: "add8e6", + lightcoral: "f08080", + lightcyan: "e0ffff", + lightgoldenrodyellow: "fafad2", + lightgray: "d3d3d3", + lightgreen: "90ee90", + lightgrey: "d3d3d3", + lightpink: "ffb6c1", + lightsalmon: "ffa07a", + lightseagreen: "20b2aa", + lightskyblue: "87cefa", + lightslategray: "789", + lightslategrey: "789", + lightsteelblue: "b0c4de", + lightyellow: "ffffe0", + lime: "0f0", + limegreen: "32cd32", + linen: "faf0e6", + magenta: "f0f", + maroon: "800000", + mediumaquamarine: "66cdaa", + mediumblue: "0000cd", + mediumorchid: "ba55d3", + mediumpurple: "9370db", + mediumseagreen: "3cb371", + mediumslateblue: "7b68ee", + mediumspringgreen: "00fa9a", + mediumturquoise: "48d1cc", + mediumvioletred: "c71585", + midnightblue: "191970", + mintcream: "f5fffa", + mistyrose: "ffe4e1", + moccasin: "ffe4b5", + navajowhite: "ffdead", + navy: "000080", + oldlace: "fdf5e6", + olive: "808000", + olivedrab: "6b8e23", + orange: "ffa500", + orangered: "ff4500", + orchid: "da70d6", + palegoldenrod: "eee8aa", + palegreen: "98fb98", + paleturquoise: "afeeee", + palevioletred: "db7093", + papayawhip: "ffefd5", + peachpuff: "ffdab9", + peru: "cd853f", + pink: "ffc0cb", + plum: "dda0dd", + powderblue: "b0e0e6", + purple: "800080", + rebeccapurple: "663399", + red: "f00", + rosybrown: "bc8f8f", + royalblue: "4169e1", + saddlebrown: "8b4513", + salmon: "fa8072", + sandybrown: "f4a460", + seagreen: "2e8b57", + seashell: "fff5ee", + sienna: "a0522d", + silver: "c0c0c0", + skyblue: "87ceeb", + slateblue: "6a5acd", + slategray: "708090", + slategrey: "708090", + snow: "fffafa", + springgreen: "00ff7f", + steelblue: "4682b4", + tan: "d2b48c", + teal: "008080", + thistle: "d8bfd8", + tomato: "ff6347", + turquoise: "40e0d0", + violet: "ee82ee", + wheat: "f5deb3", + white: "fff", + whitesmoke: "f5f5f5", + yellow: "ff0", + yellowgreen: "9acd32" +}; + +// Make it easy to access colors via `hexNames[hex]` +var hexNames = tinycolor.hexNames = flip(names); + + +// Utilities +// --------- + +// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }` +function flip(o) { + var flipped = { }; + for (var i in o) { + if (o.hasOwnProperty(i)) { + flipped[o[i]] = i; + } + } + return flipped; +} + +// Return a valid alpha value [0,1] with all invalid values being set to 1 +function boundAlpha(a) { + a = parseFloat(a); + + if (isNaN(a) || a < 0 || a > 1) { + a = 1; + } + + return a; +} + +// Take input from [0, n] and return it as [0, 1] +function bound01(n, max) { + if (isOnePointZero(n)) { n = "100%"; } + + var processPercent = isPercentage(n); + n = mathMin(max, mathMax(0, parseFloat(n))); + + // Automatically convert percentage into number + if (processPercent) { + n = parseInt(n * max, 10) / 100; + } + + // Handle floating point rounding errors + if ((math.abs(n - max) < 0.000001)) { + return 1; + } + + // Convert into [0, 1] range if it isn't already + return (n % max) / parseFloat(max); +} + +// Force a number between 0 and 1 +function clamp01(val) { + return mathMin(1, mathMax(0, val)); +} + +// Parse a base-16 hex value into a base-10 integer +function parseIntFromHex(val) { + return parseInt(val, 16); +} + +// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1 +// +function isOnePointZero(n) { + return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1; +} + +// Check to see if string passed in is a percentage +function isPercentage(n) { + return typeof n === "string" && n.indexOf('%') != -1; +} + +// Force a hex value to have 2 characters +function pad2(c) { + return c.length == 1 ? '0' + c : '' + c; +} + +// Replace a decimal with it's percentage value +function convertToPercentage(n) { + if (n <= 1) { + n = (n * 100) + "%"; + } + + return n; +} + +// Converts a decimal to a hex value +function convertDecimalToHex(d) { + return Math.round(parseFloat(d) * 255).toString(16); +} +// Converts a hex value to a decimal +function convertHexToDecimal(h) { + return (parseIntFromHex(h) / 255); +} + +var matchers = (function() { + + // + var CSS_INTEGER = "[-\\+]?\\d+%?"; + + // + var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?"; + + // Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome. + var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")"; + + // Actual matching. + // Parentheses and commas are optional, but not required. + // Whitespace can take the place of commas or opening paren + var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?"; + + return { + rgb: new RegExp("rgb" + PERMISSIVE_MATCH3), + rgba: new RegExp("rgba" + PERMISSIVE_MATCH4), + hsl: new RegExp("hsl" + PERMISSIVE_MATCH3), + hsla: new RegExp("hsla" + PERMISSIVE_MATCH4), + hsv: new RegExp("hsv" + PERMISSIVE_MATCH3), + hsva: new RegExp("hsva" + PERMISSIVE_MATCH4), + hex3: /^#?([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/, + hex6: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/, + hex8: /^#?([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/ + }; +})(); + +// `stringInputToObject` +// Permissive string parsing. Take in a number of formats, and output an object +// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}` +function stringInputToObject(color) { + + color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase(); + var named = false; + if (names[color]) { + color = names[color]; + named = true; + } + else if (color == 'transparent') { + return { r: 0, g: 0, b: 0, a: 0, format: "name" }; + } + + // Try to match string input using regular expressions. + // Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360] + // Just return an object and let the conversion functions handle that. + // This way the result will be the same whether the tinycolor is initialized with string or object. + var match; + if ((match = matchers.rgb.exec(color))) { + return { r: match[1], g: match[2], b: match[3] }; + } + if ((match = matchers.rgba.exec(color))) { + return { r: match[1], g: match[2], b: match[3], a: match[4] }; + } + if ((match = matchers.hsl.exec(color))) { + return { h: match[1], s: match[2], l: match[3] }; + } + if ((match = matchers.hsla.exec(color))) { + return { h: match[1], s: match[2], l: match[3], a: match[4] }; + } + if ((match = matchers.hsv.exec(color))) { + return { h: match[1], s: match[2], v: match[3] }; + } + if ((match = matchers.hsva.exec(color))) { + return { h: match[1], s: match[2], v: match[3], a: match[4] }; + } + if ((match = matchers.hex8.exec(color))) { + return { + a: convertHexToDecimal(match[1]), + r: parseIntFromHex(match[2]), + g: parseIntFromHex(match[3]), + b: parseIntFromHex(match[4]), + format: named ? "name" : "hex8" + }; + } + if ((match = matchers.hex6.exec(color))) { + return { + r: parseIntFromHex(match[1]), + g: parseIntFromHex(match[2]), + b: parseIntFromHex(match[3]), + format: named ? "name" : "hex" + }; + } + if ((match = matchers.hex3.exec(color))) { + return { + r: parseIntFromHex(match[1] + '' + match[1]), + g: parseIntFromHex(match[2] + '' + match[2]), + b: parseIntFromHex(match[3] + '' + match[3]), + format: named ? "name" : "hex" + }; + } + + return false; +} + +function validateWCAG2Parms(parms) { + // return valid WCAG2 parms for isReadable. + // If input parms are invalid, return {"level":"AA", "size":"small"} + var level, size; + parms = parms || {"level":"AA", "size":"small"}; + level = (parms.level || "AA").toUpperCase(); + size = (parms.size || "small").toLowerCase(); + if (level !== "AA" && level !== "AAA") { + level = "AA"; + } + if (size !== "small" && size !== "large") { + size = "small"; + } + return {"level":level, "size":size}; +} + +loadTinyColor = function(){ + return tinycolor +} \ No newline at end of file From 1f2d7262111187e1888b6e3314ed6e921a80d687 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 16:16:58 -0800 Subject: [PATCH 02/14] awesome stuff --- .../data_visualization/earthquakes_live.js | 110 +++++++++++------- 1 file changed, 65 insertions(+), 45 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 76f0fad5b0..fa464b9e13 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -1,44 +1,49 @@ -//earthquakes_live.js -//created by james b. pollack @imgntn on 12/5/2015 +// earthquakes_live.js +// exploratory implementation in prep for abstract latlong to earth graphing tool for VR +// shows all of the quakes in the past 24 hours reported by the USGS +// created by james b. pollack @imgntn on 12/5/2015 +// working notes: maybe try doing markers as boxes,rotated to the sphere normal, and with the height representing some value + Script.include('../libraries/promise.js'); var Promise = loadPromise(); Script.include('../libraries/tinyColor.js'); var tinyColor = loadTinyColor(); -var EARTH_SPHERE_RADIUS = 5; +//you could make it the size of the actual earth. +var EARTH_SPHERE_RADIUS = 6371; +var EARTH_SPHERE_RADIUS = 2; + var EARTH_CENTER_POSITION = Vec3.sum(MyAvatar.position, { - x: 5, + x: 0, y: 0, z: 0 }); -var EARTH_MODEL_URL='http://public.highfidelity.io/marketplace/hificontent/Scripts/planets/planets/earth.fbx'; +var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx'; + +var POLL_FOR_CHANGES = false; //USGS updates the data every five minutes -var CHECK_QUAKE_FREQUENCY = 300 * 1000; +var CHECK_QUAKE_FREQUENCY = 5 * 60 * 1000; var QUAKE_MARKER_DIMENSIONS = { - x: 0.1, - y: 0.1, - z: 0.1 + x: 0.01, + y: 0.01, + z: 0.01 }; function createEarth() { var earthProperties = { name: 'Earth', type: 'Model', - modelURL:EARTH_MODEL_URL, + modelURL: EARTH_MODEL_URL, position: EARTH_CENTER_POSITION, dimensions: { x: EARTH_SPHERE_RADIUS, y: EARTH_SPHERE_RADIUS, z: EARTH_SPHERE_RADIUS }, - // color: { - // red: 0, - // green: 100, - // blue: 150 - // }, + rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), collisionsWillMove: false, userData: JSON.stringify({ grabbableKey: { @@ -50,26 +55,32 @@ function createEarth() { return Entities.addEntity(earthProperties) } -function plotLatitudeLongitude(radiusOfSphere, latitude, longitude) { - var tx = radiusOfSphere * Math.cos(latitude) * Math.cos(longitude); - var ty = radiusOfSphere * -Math.sin(latitude); - var tz = radiusOfSphere * Math.cos(latitude) * Math.sin(longitude); +function latLongToVector3(lat, lon, radius, height) { + var phi = (lat) * Math.PI / 180; + var theta = (lon - 180) * Math.PI / 180; + + var x = -(radius + height) * Math.cos(phi) * Math.cos(theta); + var y = (radius + height) * Math.sin(phi); + var z = (radius + height) * Math.cos(phi) * Math.sin(theta); + return { - x: tx, - y: ty, - z: tz - } + x: x, + y: y, + z: z + }; } function getQuakePosition(earthquake) { - var latitude = earthquake.geometry.coordinates[0]; - var longitude = earthquake.geometry.coordinates[1]; - var latlng = plotLatitudeLongitude(2.5, latitude, longitude); + var longitude = earthquake.geometry.coordinates[0]; + var latitude = earthquake.geometry.coordinates[1]; + var depth = earthquake.geometry.coordinates[2]; + + var latlng = latLongToVector3(latitude, longitude, EARTH_SPHERE_RADIUS / 2, 0); var position = EARTH_CENTER_POSITION; var finalPosition = Vec3.sum(position, latlng); - // print('finalpos::' + JSON.stringify(finalPosition)) + print('finalpos::' + JSON.stringify(finalPosition)) return finalPosition } @@ -95,14 +106,9 @@ function get(url) { }); } -function showEarthquake(snapshot) { - var earthquake = snapshot.val(); - print("Mag " + earthquake.mag + " at " + earthquake.place); -} - function createQuakeMarker(earthquake) { var markerProperties = { - name: 'Marker', + name: earthquake.properties.place, type: 'Sphere', dimensions: QUAKE_MARKER_DIMENSIONS, position: getQuakePosition(earthquake), @@ -110,7 +116,7 @@ function createQuakeMarker(earthquake) { color: getQuakeMarkerColor(earthquake) } - //print('marker properties::' + JSON.stringify(markerProperties)) + print('marker properties::' + JSON.stringify(markerProperties)) return Entities.addEntity(markerProperties); } @@ -137,7 +143,7 @@ function scale(value, min1, max1, min2, max2) { function processQuakes(earthquakes) { print('quakers length' + earthquakes.length) earthquakes.forEach(function(quake) { - // print('PROCESSING A QUAKE') + // print('PROCESSING A QUAKE') var marker = createQuakeMarker(quake); markers.push(marker); }) @@ -147,16 +153,18 @@ function processQuakes(earthquakes) { var quakea; var markers = []; -var earth = createEarth(); +var earth = createEarth(); -get(QUAKE_URL).then(function(response) { - print('got it::' + response.features.length) - quakes = response.features; - processQuakes(quakes); - //print("Success!" + JSON.stringify(response)); -}, function(error) { - print('error getting quakes') -}); +function getThenProcessQuakes() { + get(QUAKE_URL).then(function(response) { + print('got it::' + response.features.length) + quakes = response.features; + processQuakes(quakes); + //print("Success!" + JSON.stringify(response)); + }, function(error) { + print('error getting quakes') + }); +} function cleanupMarkers() { print('CLEANING UP MARKERS') @@ -170,4 +178,16 @@ function cleanupEarth() { } Script.scriptEnding.connect(cleanupMarkers); -Script.scriptEnding.connect(cleanupEarth); \ No newline at end of file +Script.scriptEnding.connect(cleanupEarth); + +//first draw +getThenProcessQuakes(); + + +var pollingInterval; +if (POLL_FOR_CHANGES === true) { + pollingInterval = Script.setInterval(function() { + cleanupMarkers(); + getThenProcessQuakes() + }, CHECK_QUAKE_FREQUENCY) +} \ No newline at end of file From f33f4cbcaefb08ae885baabf20f59d555189d58f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 16:22:17 -0800 Subject: [PATCH 03/14] cleanup --- examples/data_visualization/testQuakes.json | 153 -------------------- 1 file changed, 153 deletions(-) delete mode 100644 examples/data_visualization/testQuakes.json diff --git a/examples/data_visualization/testQuakes.json b/examples/data_visualization/testQuakes.json deleted file mode 100644 index a6429b79c8..0000000000 --- a/examples/data_visualization/testQuakes.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "type": "FeatureCollection", - "metadata": { - "generated": 1449347736000, - "url": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/all_hour.geojson", - "title": "USGS All Earthquakes, Past Hour", - "status": 200, - "api": "1.1.0", - "count": 4 - }, - "features": [{ - "type": "Feature", - "properties": { - "mag": 1.39, - "place": "17km ESE of Julian, California", - "time": 1449345604050, - "updated": 1449345856673, - "tz": -480, - "url": "http://earthquake.usgs.gov/earthquakes/eventpage/ci37498832", - "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/ci37498832.geojson", - "felt": null, - "cdi": null, - "mmi": null, - "alert": null, - "status": "automatic", - "tsunami": 0, - "sig": 30, - "net": "ci", - "code": "37498832", - "ids": ",ci37498832,", - "sources": ",ci,", - "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", - "nst": 32, - "dmin": 0.1379, - "rms": 0.21, - "gap": 68, - "magType": "ml", - "type": "earthquake", - "title": "M 1.4 - 17km ESE of Julian, California" - }, - "geometry": { - "type": "Point", - "coordinates": [-116.4293333, 33.0301667, 7.84] - }, - "id": "ci37498832" - }, { - "type": "Feature", - "properties": { - "mag": 1.12, - "place": "3km ESE of The Geysers, California", - "time": 1449344686690, - "updated": 1449344783260, - "tz": -480, - "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564866", - "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564866.geojson", - "felt": null, - "cdi": null, - "mmi": null, - "alert": null, - "status": "automatic", - "tsunami": 0, - "sig": 19, - "net": "nc", - "code": "72564866", - "ids": ",nc72564866,", - "sources": ",nc,", - "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", - "nst": 11, - "dmin": 0.01342, - "rms": 0.03, - "gap": 154, - "magType": "md", - "type": "earthquake", - "title": "M 1.1 - 3km ESE of The Geysers, California" - }, - "geometry": { - "type": "Point", - "coordinates": [-122.7176666, 38.7598343, 1.48] - }, - "id": "nc72564866" - }, { - "type": "Feature", - "properties": { - "mag": 1.99, - "place": "3km SE of The Geysers, California", - "time": 1449344287500, - "updated": 1449344383210, - "tz": -480, - "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564856", - "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564856.geojson", - "felt": null, - "cdi": null, - "mmi": null, - "alert": null, - "status": "automatic", - "tsunami": 0, - "sig": 61, - "net": "nc", - "code": "72564856", - "ids": ",nc72564856,", - "sources": ",nc,", - "types": ",general-link,geoserve,nearby-cities,origin,phase-data,", - "nst": 13, - "dmin": 0.01626, - "rms": 0.02, - "gap": 72, - "magType": "md", - "type": "earthquake", - "title": "M 2.0 - 3km SE of The Geysers, California" - }, - "geometry": { - "type": "Point", - "coordinates": [-122.7203369, 38.7571678, 0.3] - }, - "id": "nc72564856" - }, { - "type": "Feature", - "properties": { - "mag": 2.36, - "place": "3km ESE of The Geysers, California", - "time": 1449344196380, - "updated": 1449344459434, - "tz": -480, - "url": "http://earthquake.usgs.gov/earthquakes/eventpage/nc72564836", - "detail": "http://earthquake.usgs.gov/earthquakes/feed/v1.0/detail/nc72564836.geojson", - "felt": 0, - "cdi": 1, - "mmi": null, - "alert": null, - "status": "automatic", - "tsunami": 0, - "sig": 86, - "net": "nc", - "code": "72564836", - "ids": ",nc72564836,", - "sources": ",nc,", - "types": ",dyfi,focal-mechanism,general-link,geoserve,nearby-cities,origin,phase-data,scitech-link,", - "nst": 36, - "dmin": 0.01395, - "rms": 0.07, - "gap": 38, - "magType": "md", - "type": "earthquake", - "title": "M 2.4 - 3km ESE of The Geysers, California" - }, - "geometry": { - "type": "Point", - "coordinates": [-122.722168, 38.7598343, 1.21] - }, - "id": "nc72564836" - }], - "bbox": [-122.722168, 33.0301667, 0.3, -116.4293333, 38.7598343, 7.84] -} \ No newline at end of file From 4999b34327822655d2063d2ec1bac96949997ead Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 16:54:55 -0800 Subject: [PATCH 04/14] cleanup interval --- examples/data_visualization/earthquakes_live.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index fa464b9e13..cb88ec33dc 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -80,7 +80,7 @@ function getQuakePosition(earthquake) { var position = EARTH_CENTER_POSITION; var finalPosition = Vec3.sum(position, latlng); - print('finalpos::' + JSON.stringify(finalPosition)) + //print('finalpos::' + JSON.stringify(finalPosition)) return finalPosition } @@ -116,7 +116,7 @@ function createQuakeMarker(earthquake) { color: getQuakeMarkerColor(earthquake) } - print('marker properties::' + JSON.stringify(markerProperties)) + // print('marker properties::' + JSON.stringify(markerProperties)) return Entities.addEntity(markerProperties); } @@ -177,14 +177,20 @@ function cleanupEarth() { Entities.deleteEntity(earth); } +function cleanupInterval() { + if (pollingInterval !== null) { + Script.clearInterval(pollingInterval) + } +} + Script.scriptEnding.connect(cleanupMarkers); Script.scriptEnding.connect(cleanupEarth); - +Script.scriptEnding.connect(cleanupInterval); //first draw getThenProcessQuakes(); +var pollingInterval = null; -var pollingInterval; if (POLL_FOR_CHANGES === true) { pollingInterval = Script.setInterval(function() { cleanupMarkers(); From 4a5b9bd523391320a08aa203091c24b74779c2fa Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 16:57:09 -0800 Subject: [PATCH 05/14] start position --- examples/data_visualization/earthquakes_live.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index cb88ec33dc..eae1333089 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -14,11 +14,17 @@ var tinyColor = loadTinyColor(); var EARTH_SPHERE_RADIUS = 6371; var EARTH_SPHERE_RADIUS = 2; -var EARTH_CENTER_POSITION = Vec3.sum(MyAvatar.position, { +var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, { x: 0, - y: 0, + y: 0.5, z: 0 -}); +}), Vec3.multiply(EARTH_SPHERE_RADIUS, Quat.getFront(Camera.getOrientation()))); + +// var EARTH_CENTER_POSITION = Vec3.sum(MyAvatar.position, { +// x: 0, +// y: 0, +// z: 0 +// }); var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx'; From 7fc3d0f34edebde14f630bfea9c608fa3e382142 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 16:58:50 -0800 Subject: [PATCH 06/14] header --- examples/data_visualization/earthquakes_live.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index eae1333089..3090b00e44 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -1,7 +1,13 @@ // earthquakes_live.js +// // exploratory implementation in prep for abstract latlong to earth graphing tool for VR // shows all of the quakes in the past 24 hours reported by the USGS +// // created by james b. pollack @imgntn on 12/5/2015 +// Copyright 2015 High Fidelity, Inc. +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// // working notes: maybe try doing markers as boxes,rotated to the sphere normal, and with the height representing some value Script.include('../libraries/promise.js'); From d518def97477cd11e45fecdc842e47c220b68f39 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:01:54 -0800 Subject: [PATCH 07/14] poll by default --- examples/data_visualization/earthquakes_live.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 3090b00e44..2457a0646a 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -34,7 +34,7 @@ var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, { var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx'; -var POLL_FOR_CHANGES = false; +var POLL_FOR_CHANGES = true; //USGS updates the data every five minutes var CHECK_QUAKE_FREQUENCY = 5 * 60 * 1000; From 945b5bd00f5c9d0a1caf2c5bf90347ac125411fe Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:11:27 -0800 Subject: [PATCH 08/14] cleanup --- examples/data_visualization/earthquakes_live.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 2457a0646a..826c833243 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -26,12 +26,6 @@ var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, { z: 0 }), Vec3.multiply(EARTH_SPHERE_RADIUS, Quat.getFront(Camera.getOrientation()))); -// var EARTH_CENTER_POSITION = Vec3.sum(MyAvatar.position, { -// x: 0, -// y: 0, -// z: 0 -// }); - var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx'; var POLL_FOR_CHANGES = true; From a899fe0e1b3eacca35d79c2cd0b4ce889d0dae5f Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:26:11 -0800 Subject: [PATCH 09/14] fix typo --- examples/data_visualization/earthquakes_live.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 826c833243..272c933696 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -44,6 +44,7 @@ function createEarth() { type: 'Model', modelURL: EARTH_MODEL_URL, position: EARTH_CENTER_POSITION, + collisionsWillMove:true, dimensions: { x: EARTH_SPHERE_RADIUS, y: EARTH_SPHERE_RADIUS, @@ -116,6 +117,7 @@ function createQuakeMarker(earthquake) { var markerProperties = { name: earthquake.properties.place, type: 'Sphere', + parentID:earth, dimensions: QUAKE_MARKER_DIMENSIONS, position: getQuakePosition(earthquake), lifetime: 6000, @@ -156,7 +158,7 @@ function processQuakes(earthquakes) { print('markers length:' + markers.length) } -var quakea; +var quakes; var markers = []; var earth = createEarth(); From ade32cbf6137fada8eb5661c846779ccd88bc015 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:30:38 -0800 Subject: [PATCH 10/14] remove collisions and add parenting --- examples/data_visualization/earthquakes_live.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 272c933696..69d639238c 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -44,7 +44,6 @@ function createEarth() { type: 'Model', modelURL: EARTH_MODEL_URL, position: EARTH_CENTER_POSITION, - collisionsWillMove:true, dimensions: { x: EARTH_SPHERE_RADIUS, y: EARTH_SPHERE_RADIUS, From 30b32765b915f7e4f5947883fdfc601e4bae9f86 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:32:02 -0800 Subject: [PATCH 11/14] make earth grabbable to show parenting --- examples/data_visualization/earthquakes_live.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 69d639238c..579c981b2d 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -51,11 +51,11 @@ function createEarth() { }, rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), collisionsWillMove: false, - userData: JSON.stringify({ - grabbableKey: { - grabbable: false - } - }) + // userData: JSON.stringify({ + // grabbableKey: { + // grabbable: false + // } + // }) } return Entities.addEntity(earthProperties) From 906540272ec77dfa1cb96ae46cff1d6de8004242 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:39:43 -0800 Subject: [PATCH 12/14] disable collisions for now because of shapetype --- examples/data_visualization/earthquakes_live.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 579c981b2d..3498885ff6 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -50,7 +50,9 @@ function createEarth() { z: EARTH_SPHERE_RADIUS }, rotation: Quat.fromPitchYawRollDegrees(0, 90, 0), - collisionsWillMove: false, + // collisionsWillMove: true, + //if you have a shapetype it blocks the smaller markers + // shapeType:'sphere' // userData: JSON.stringify({ // grabbableKey: { // grabbable: false @@ -119,6 +121,7 @@ function createQuakeMarker(earthquake) { parentID:earth, dimensions: QUAKE_MARKER_DIMENSIONS, position: getQuakePosition(earthquake), + ignoreForCollisions:true, lifetime: 6000, color: getQuakeMarkerColor(earthquake) } From 7859241ad31d1ad467eb22e1b4512f6178f57e10 Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:43:13 -0800 Subject: [PATCH 13/14] spin the earth for fun --- .../data_visualization/earthquakes_live.js | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index 3498885ff6..f625f9afb0 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -185,6 +185,7 @@ function cleanupMarkers() { function cleanupEarth() { Entities.deleteEntity(earth); + Script.update.disconnect(spinEarth); } function cleanupInterval() { @@ -196,7 +197,7 @@ function cleanupInterval() { Script.scriptEnding.connect(cleanupMarkers); Script.scriptEnding.connect(cleanupEarth); Script.scriptEnding.connect(cleanupInterval); -//first draw + getThenProcessQuakes(); var pollingInterval = null; @@ -206,4 +207,17 @@ if (POLL_FOR_CHANGES === true) { cleanupMarkers(); getThenProcessQuakes() }, CHECK_QUAKE_FREQUENCY) -} \ No newline at end of file +} + + +function spinEarth(){ +Entities.editEntity(earth,{ + angularVelocity:{ + x:0, + y:0.5, + z:0 + } +}) +} + +Script.update.connect(spinEarth) \ No newline at end of file From 378b71d7011a097262f5f31764981c99551606fa Mon Sep 17 00:00:00 2001 From: "James B. Pollack" Date: Sat, 5 Dec 2015 17:51:55 -0800 Subject: [PATCH 14/14] dont spin by default --- examples/data_visualization/earthquakes_live.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/examples/data_visualization/earthquakes_live.js b/examples/data_visualization/earthquakes_live.js index f625f9afb0..8594f827a0 100644 --- a/examples/data_visualization/earthquakes_live.js +++ b/examples/data_visualization/earthquakes_live.js @@ -28,6 +28,7 @@ var EARTH_CENTER_POSITION = Vec3.sum(Vec3.sum(MyAvatar.position, { var EARTH_MODEL_URL = 'http://hifi-content.s3.amazonaws.com/james/earthquakes_live/models/earth-noclouds.fbx'; +var SHOULD_SPIN=false; var POLL_FOR_CHANGES = true; //USGS updates the data every five minutes var CHECK_QUAKE_FREQUENCY = 5 * 60 * 1000; @@ -214,10 +215,12 @@ function spinEarth(){ Entities.editEntity(earth,{ angularVelocity:{ x:0, - y:0.5, + y:0.25, z:0 } }) } -Script.update.connect(spinEarth) \ No newline at end of file +if(SHOULD_SPIN===true){ + Script.update.connect(spinEarth); +}