diff --git a/.gitignore b/.gitignore index 300e949..260ffcb 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,8 @@ develop-eggs lib lib64 __pycache__ - +node_modules/ +bower_components/ # Installer logs pip-log.txt diff --git a/index.yaml b/index.yaml index 194874d..baf29a7 100644 --- a/index.yaml +++ b/index.yaml @@ -119,6 +119,11 @@ indexes: - name: user - name: name +- kind: Fluid + properties: + - name: user + - name: user + - kind: Fluid ancestor: yes properties: @@ -130,3 +135,13 @@ indexes: - name: user - name: date direction: desc + +- kind: ImageModel + properties: + - name: user + - name: user + +- kind: Rock + properties: + - name: user + - name: user diff --git a/page_handlers.py b/page_handlers.py index d66bc82..f0e6826 100644 --- a/page_handlers.py +++ b/page_handlers.py @@ -1356,7 +1356,7 @@ def get(self, user): if self.request.get("error"): params.update(error="Invalid image file") - template = env.get_template('model.html') + template = env.get_template('model2.html') html = template.render(params) self.response.out.write(html) diff --git a/static/css/modelr.css b/static/css/modelr.css index 0ddff59..47414ae 100644 --- a/static/css/modelr.css +++ b/static/css/modelr.css @@ -1,3 +1,97 @@ +/* NG */ +[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { + display: none !important; +} +/*************/ +/* 2D Models */ +.model_info { + color: #aaa; + font-size: 200%; + vertical-align: middle; + top:4px; +} + +input { + text-align: end; +} + +.seismic-color { + height: 10px; + margin: 0; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); +} + +.alert-success { + width:50%; + height: 60px; + position: fixed; + right: 10px; + top: 10px; + z-index: 1000; +} + +.form-control { + margin-bottom: 5px; +} + +.flexslider .slides img { + max-height: 400px; +} + +canvas { + position: absolute; +} + +svg { + position: relative; + z-index: 3; +} +.remove-padding { + padding:0; +} + +.bottom-buffer { + margin-bottom:20px; +} + +.model_select { + text-align: right; + float: right; + padding-right: 20px; +} + +.flex-direction-nav a { + height: 50px; +} + +.dropdown-menu { + text-align: center; +} + +.model_button { + float: right; + margin: 5px; +} + +.model_select { + float: right; + margin-bottom: 5px; +} + +.rock-color { + height: 34px; + margin: 0 5px 5px; + border: 1px solid #ccc; + border-radius: 4px; + box-shadow: inset 0 1px 1px rgba(0,0,0,.075); +} + +/*************/ + +/* 1D Models */ + .tick line{ opacity: 0.1; } @@ -22,6 +116,8 @@ div.plot { stroke: #000; shape-rendering: crispEdges; } +/**************/ + .ribbon-banner { display: block; position: absolute; diff --git a/static/js/1D_model.js b/static/js/1D_model.js index d6bbd75..8e95ef4 100644 --- a/static/js/1D_model.js +++ b/static/js/1D_model.js @@ -56,22 +56,22 @@ setup1D = function(rock_div, var earth_model = rock_core.intervals(); - // var vpPlot = undefined - // tPlot = undefined, - // rhoPlot = undefined, - // vPPlot = undefined, - // zPPlot = undefined, - // vpLog = undefined, - // vsLog = undefined, - // rhoLog = undefined, - // zPLog = undefined, - // synthLog = undefined; + var vpPlot = undefined + tPlot = undefined, + rhoPlot = undefined, + vPPlot = undefined, + zPPlot = undefined, + vpLog = undefined, + vsLog = undefined, + rhoLog = undefined, + zPLog = undefined, + synthLog = undefined; var vpPlot, tPlot, rhoPlot, vPPlot, VPPlot, vpLog, vsLog, rhoLog, zPLog, SynthLog; $.ajax(server + "/data.json?type=seismic&script=fluid_sub.py", {type: "GET", data: {payload: JSON.stringify({seismic: seismic, earth_model: earth_model})}, success: function success(data){ - + console.log(data); var width = 120; //Create VP Plot vpPlot = g3.plot('.plot') @@ -220,8 +220,6 @@ setup1D = function(rock_div, }); - //update_data(); - function update_data(){ var offset = 10; var frequency = $("#frequency").val(); @@ -266,4 +264,4 @@ setup1D = function(rock_div, } ); }; // end of function update_data -}; +}; \ No newline at end of file diff --git a/static/js/app.js b/static/js/app.js new file mode 100644 index 0000000..a9fffe0 --- /dev/null +++ b/static/js/app.js @@ -0,0 +1,780 @@ +'use strict'; +var app = angular.module('modelr', + ['mgcrea.ngStrap', + 'ngAnimate', + 'angular-flexslider']); + + +app.config(['$interpolateProvider', function($interpolateProvider) { + $interpolateProvider.startSymbol('{['); + $interpolateProvider.endSymbol(']}'); +}]); + + +app.controller('2DCtrl', function ($scope, $http, $alert, $timeout) { + + $scope.setDefaults = function(){ + $scope.zDomain = ['depth','time']; + $scope.zAxisDomain = 'depth'; + $scope.zRange = 1000; + + // TODO update from mouse over on seismic plots + $scope.trace = 1; + $scope.traceStr = "1"; + $scope.offset = 1; + $scope.offsetNum = 3; + $scope.offsetStr = "1"; + $scope.twt = 1; + $scope.twtStr = "1"; + $scope.gain = 1; + $scope.gainStr = "1"; + $scope.maxGain = "10"; + $scope.frequency = 20; + $scope.phase = 180.0; + $scope.phaseStr = "180"; + $scope.snr = 3.0; + $scope.snrStr = "3.0" + $scope.frequencyNum = 20.72; + $scope.colorRange = ['#FF0000', '#FFF', '#0000FF']; + + // TODO get from app before so we get the prod url + $scope.server = 'http://localhost:8081'; + + $scope.theta = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]; + $scope.popover = { + title: "Models", + content: "Choose a model framework from the carousel below, or use the buttons to the right to upload an image or create a new model with the model builder. then assign the model's rocks and other parameters in the panel to the right." + }; + }; + + $scope.setColorPickers = function(){ + for(var i = 0; i < $scope.colorRange.length; i++){ + var colorp = $('.s-color-' + i).colorpicker() + .on('changeColor.colorpicker', function(event){ + var index = event.currentTarget.attributes["data-index"].value; + $(event.currentTarget).css('background-color', $scope.colorRange[index]); + $scope.colorRange[index] = event.color.toHex(); + console.log($scope.colorRange); + }); + } + }; + + $scope.removeColor = function(index){ + $scope.colorRange.splice(index, 1); + $scope.colorDomain.splice(index, 1); + }; + + $scope.addColor = function(){ + $scope.colorRange.push('#FFF'); + $scope.colorDomain.push(0); + $timeout($scope.setColorPickers, 300); + }; + + $scope.fetchImageModels = function(){ + $http.get('/image_model?all') + .then(function(response) { + $scope.images = response.data; + + if($scope.images.length > 0){ + $scope.curImage = $scope.images[0]; + + + for(var i = 0; i < $scope.images.length; i++){ + $scope.images[i].rocks = []; + for(var j = 0; j < $scope.images[i].colours.length; j++){ + var rand = $scope.rocks[Math.floor(Math.random() * $scope.rocks.length)]; + $scope.images[i].rocks.push(rand); + } + } + } + } + ); + }; + + $scope.fetchRocks = function(){ + $http.get('/rock?all'). + then(function(response) { + $scope.rocks = response.data; + } + ); + }; + + $scope.loadSaved = function(){ + + var array = $.map($scope.savedEarthModel.mapping, function(value, index) { + return [value]; + }); + + $scope.curImage.rocks = []; + for(var i = 0; i < array.length; i++){ + $scope.curImage.rocks.push(array[i].rock); + } + console.log($scope.curImage.rocks); + }; + + $scope.slideClick = function(slider){ + $scope.curImage = $scope.images[slider.element.currentSlide]; + }; + + $scope.update_data = function(){ + var earth_model = $scope.makeEarthModelStruct(); + + var seismic = { + frequency: $scope.frequency, + wavelet: "ricker", + dt: 0.001, + phase: $scope.phase, + snr: $scope.snr + }; + + var data = { + seismic: seismic, + earth_model: earth_model, + trace: $scope.trace, + offset: $scope.offset + }; + + var payload = JSON.stringify(data); + $http.get($scope.server + '/data.json?type=seismic&script=convolution_model.py&payload=' + payload) + .then(function(response){ + console.log(response.data); + $scope.plot(response.data); + $scope.maxTrace = String(response.data.seismic.length - 1); + $scope.maxTWT = String(response.data.seismic[0].length - 1); + $scope.maxOffset = String(response.data.offset_gather.length - 1); + $scope.updateClicked = true; + } + ); + }; + + $scope.changeTraceStr = function(){ + $scope.trace = Number($scope.traceStr); + var arr = [$scope.data.seismic[$scope.trace]]; + $scope.vDLog + .setXMin($scope.trace) + .reDraw( + arr, + [0, $scope.data.seismic.length - 1], + [0, $scope.data.seismic[0].length - 1] + ); + }; + + $scope.changeTWTStr = function(){ + $scope.twt = Number($scope.twtStr); + $scope.updateTWT(); + }; + + $scope.getMaxAll = function(){ + var max = []; + var maxT = getMax(d3.max($scope.aTArr) * $scope.gain, d3.min($scope.aTArr) * $scope.gain); + max.push(maxT); + var maxO = getMax(d3.max($scope.aOArr) * $scope.gain, d3.min($scope.aOArr) * $scope.gain); + max.push(maxO); + var maxF = getMax(d3.max($scope.aFArr) * $scope.gain, d3.min($scope.aFArr) * $scope.gain); + max.push(maxF); + return d3.max(max); + }; + + $scope.updateTWT = function(){ + // Update Horizons + var yScale = $scope.vDPlot.yScale; + $scope.vDHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + yScale = $scope.oGPlot.yScale; + $scope.oGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + yScale = $scope.wGPlot.yScale; + $scope.wGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + $scope.aTArr = getCrossSection($scope.data.seismic, $scope.twt); + $scope.aOArr = getCrossSection($scope.data.offset_gather, $scope.twt); + $scope.aFArr = getCrossSection($scope.data.wavelet_gather, $scope.twt); + + var maxAll = $scope.getMaxAll(); + + if(maxAll > 1){ + $scope.updateAmpPlots(maxAll); + } + + $scope.aTHor + .setGain($scope.gain) + .reDraw($scope.aTArr); + + $scope.aOHor + .setGain($scope.gain) + .reDraw($scope.aOArr); + + $scope.aFHor + .setGain($scope.gain) + .reDraw($scope.aFArr); + }; + + $scope.updateAmpPlots = function(maxAll){ + $scope.aTPlot + .reDraw( + [0, $scope.data.seismic.length - 1], + [maxAll,-maxAll], + [0, $scope.data.seismic.length - 1], + [maxAll,-maxAll] + ); + $scope.aOPlot + .reDraw( + [0, $scope.data.offset_gather.length - 1], + [maxAll,-maxAll], + [0, $scope.data.offset_gather.length - 1], + [maxAll,-maxAll] + ); + + $scope.aFPlot + .reDraw( + [0, $scope.data.wavelet_gather.length - 1], + [maxAll,-maxAll], + [0, $scope.data.wavelet_gather.length - 1], + [maxAll,-maxAll] + ); + }; + + $scope.changeGainStr = function(){ + $scope.gain = Number($scope.gainStr); + $scope.updateTWT(); + }; + + $scope.changeOffsetStr = function(){ + $scope.offset = Number($scope.offsetStr); + $scope.offsetNum = $scope.offset * 3; + var arr = [$scope.data.offset_gather[$scope.offset]]; + $scope.oGLog + .setXMin($scope.offset) + .reDraw( + arr, + [0, $scope.data.offset_gather.length - 1], + [0, $scope.data.offset_gather[0].length - 1] + ); + }; + + $scope.changeFrequencyStr = function(){ + $scope.frequency = Number($scope.frequencyStr); + $scope.frequencyNum = $scope.data.f[$scope.frequency]; + var arr = [$scope.data.wavelet_gather[$scope.frequency]]; + $scope.wGLog + .setXMin($scope.frequency) + .reDraw( + arr, + [0, $scope.data.wavelet_gather.length - 1], + [0, $scope.data.wavelet_gather[0].length - 1] + ); + }; + + $scope.changePhaseStr = function(){ + $scope.phase = Number($scope.phaseStr); + }; + + $scope.changeNoiseStr = function(){ + $scope.snr = Number($scope.snrStr); + }; + + $scope.plotSeismic = function(data, height, max){ + // Variable Density Plot + var width = $('.vd_plot').width(); + if(!$scope.vDPlot){ + $scope.vDPlot = g3.plot('.vd_plot') + .setHeight(height) + .setXTitle("spatial cross-section") + .setYTitle("time [ms]") + .setWidth(width - 30) + .toggleX2Axis(true) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setY2TickFormat("") + .setDuration(5) + .setMargin(20,10,5,40) + .setXDomain([0, data.seismic.length - 1]) + .setYDomain([0, data.seismic[0].length - 1]) + .setX2Domain([0, data.seismic.length - 1]) + .setY2Domain([0, data.seismic[0].length - 1]) + .draw(); + } else { + $scope.vDPlot.reDraw( + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1], + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1] + ); + } + + // Draw Seismic Image + if(!$scope.seis){ + $scope.seis = g3.seismic($scope.vDPlot, data.seismic) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.seis + .setGain($scope.gain) + .setColor($scope.colorScale) + .reDraw(data.seismic); + } + }; + + $scope.plotOffset = function(data, height, max){ + // Offset Gather Plot + var width = $('.og_plot').width(); + if(!$scope.oGPlot){ + $scope.oGPlot = g3.plot('.og_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setXTitle("angle gather") + .toggleX2Axis(true) + .setX2Ticks(6) + .setXTicks(6) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(20,10,5,30) + .setXDomain([0, data.offset_gather.length - 1]) + .setYDomain([0, data.offset_gather[0].length - 1]) + .setX2Domain([0, data.offset_gather.length - 1]) + .setY2Domain([0, data.offset_gather[0].length - 1]) + .draw(); + } else { + $scope.oGPlot.reDraw( + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1], + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1] + ); + } + + // Draw Offset Gather Image + if(!$scope.og){ + $scope.og = g3.seismic($scope.oGPlot, data.offset_gather) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.og + .setGain($scope.gain) + .setColor($scope.colorScale) + .reDraw(data.offset_gather); + } + }; + + $scope.plotWavelet = function(data, height, max){ + // Wavelet Gather Plot + var width = $('.wg_plot').width(); + if(!$scope.wGPlot){ + $scope.wGPlot = g3.plot('.wg_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setXTitle("wavelet gather") + .toggleX2Axis(true) + .setX2Ticks(6) + .setXTicks(6) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(20,10,5,20) + .setXDomain([0, data.wavelet_gather.length - 1]) + .setYDomain([0, data.wavelet_gather[0].length - 1]) + .setX2Domain([0, data.wavelet_gather.length - 1]) + .setY2Domain([0, data.wavelet_gather[0].length - 1]) + .draw(); + } else { + $scope.wGPlot.reDraw( + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1], + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1] + ); + } + + // Draw Wavelet Gather Image + if(!$scope.wg){ + $scope.wg = g3.seismic($scope.wGPlot, data.wavelet_gather) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.wg + .setColor($scope.colorScale) + .setGain($scope.gain) + .reDraw(data.wavelet_gather); + } + }; + + $scope.plotAmplitudeTrace = function(data, height){ + // Amplitude Trace + var width = $('.at_plot').width(); + + if(!$scope.aTPlot){ + $scope.aTPlot = g3.plot('.at_plot') + .setHeight(height) + .setYTitle("amplitude") + .setDuration(5) + .setWidth(width - 30) + .setYTicks(6) + .setY2Ticks(6) + .setX2Title("trace") + .toggleX2Axis(true) + .toggleY2Axis(true) + .setXTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,40) + .setXDomain([0, $scope.data.seismic.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, $scope.data.seismic.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aTArr = getCrossSection($scope.data.seismic, $scope.twt); + + if(!$scope.aTHor){ + $scope.aTHor = g3.horizon($scope.aTPlot, $scope.aTArr) + .setDuration(5) + .draw(); + } else { + $scope.aTHor.reDraw($scope.aTArr); + } + }; + + $scope.changeColor = function(index){ + console.log($scope.colorDomain); + }; + + $scope.plotAmplitudeOffset = function(data, height){ + // Amplitude Offset Plot + var width = $('.ao_plot').width(); + if(!$scope.aOPlot){ + $scope.aOPlot = g3.plot('.ao_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .toggleX2Axis(true) + .setX2Title("\u03B8" + "\u00B0") + .setY2Ticks(6) + .setYTicks(6) + .setXTicks(6) + .setX2Ticks(6) + .toggleY2Axis(true) + .setX2TickFormat(function(d, i){ + return $scope.data.theta[d]; + }) + .setXTickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,30) + .setXDomain([0, data.offset_gather.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, data.offset_gather.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aOArr = getCrossSection($scope.data.offset_gather, $scope.twt); + + if(!$scope.aOHor){ + $scope.aOHor = g3.horizon($scope.aOPlot, $scope.aOArr) + .setDuration(5) + .draw(); + } else { + $scope.aOHor.reDraw($scope.aOArr); + } + }; + + $scope.plotAmplitudeFreq = function(data, height){ + + // Amplitude Freq Plot + var width = $('.af_plot').width(); + if(!$scope.aFPlot){ + $scope.aFPlot = g3.plot('.af_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setX2Title("centre frequency Hz") + .toggleX2Axis(true) + .setY2Ticks(6) + .setYTicks(6) + .setXTicks(6) + .setX2Ticks(6) + .toggleY2Axis(true) + .setX2TickFormat(function(d, i){ + return Math.floor($scope.data.f[d]); + }) + .setXTickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,20) + .setXDomain([0, $scope.data.wavelet_gather.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, $scope.data.wavelet_gather.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aFArr = getCrossSection($scope.data.wavelet_gather, $scope.twt); + + if(!$scope.aFHor){ + $scope.aFHor = g3.horizon($scope.aFPlot, $scope.aFArr) + .setDuration(5) + .draw(); + } else { + $scope.aFHor.reDraw($scope.aFArr); + } + }; + + $scope.plotVDWiggle = function(data){ + // Draw VD Plot Wiggle + var arr = [data.seismic[$scope.trace]]; + if(!$scope.vDLog){ + $scope.vDLog = g3.wiggle($scope.vDPlot, arr) + .setXMin($scope.trace) + .setGain(80) + .setDuration(5) + .draw(); + } else { + $scope.vDLog + .setXMin($scope.trace) + .reDraw( + arr, + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1] + ); + } + }; + + $scope.plotVDHorizon = function(data){ + var xScale = $scope.vDPlot.xScale; + var yScale = $scope.vDPlot.yScale; + if(!$scope.vDHor){ + $scope.vDHor = $scope.vDPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.seismic.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.vDHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plotOffsetWiggle = function(data){ + // Draw Offset Wiggle + var arr = [data.offset_gather[$scope.offset]]; + if(!$scope.oGLog){ + $scope.oGLog = g3.wiggle($scope.oGPlot, arr) + .setXMin($scope.offset) + .setGain(5) + .setDuration(5) + .draw(); + } else { + $scope.oGLog + .setXMin($scope.offset) + .reDraw( + arr, + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1] + ); + } + }; + + $scope.plotWaveletWiggle = function(data){ + // Draw Offset Wiggle + var arr = [data.wavelet_gather[$scope.frequency]]; + if(!$scope.wGLog){ + $scope.wGLog = g3.wiggle($scope.wGPlot, arr) + .setXMin($scope.frequency) + .setGain(22.7) + .setDuration(5) + .draw(); + } else { + $scope.wGLog + .setXMin($scope.frequency) + .reDraw( + arr, + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1] + ); + } + }; + + $scope.plotOffsetHorizon = function(data){ + // Draw Offset Horizon + var xScale = $scope.oGPlot.xScale; + var yScale = $scope.oGPlot.yScale; + if(!$scope.oGHor){ + $scope.oGHor = $scope.oGPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.offset_gather.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.oGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plotWaveletHorizon = function(data){ + // Draw Wavelet Horizon + var xScale = $scope.wGPlot.xScale; + var yScale = $scope.wGPlot.yScale; + if(!$scope.wGHor){ + $scope.wGHor = $scope.wGPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.wavelet_gather.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.wGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plot = function(data){ + $scope.data = data; + var height = 450; + var max = getMax(data.max, data.min); + max = Number(max.toFixed(2)); + + if(!$scope.colorDomain){ + $scope.colorDomain = [-max, 0, max]; + } + + $scope.colorScale = d3.scale.linear().domain($scope.colorDomain).range($scope.colorRange); + $scope.plotSeismic(data, height, max); + $scope.plotOffset(data, height, max); + $scope.plotWavelet(data, height, max); + + height = 150; + $scope.plotAmplitudeTrace(data, height); + $scope.plotAmplitudeOffset(data, height); + $scope.plotAmplitudeFreq(data, height); + $scope.plotVDWiggle(data); + $scope.plotOffsetWiggle(data); + $scope.plotWaveletWiggle(data, height); + $scope.plotVDHorizon(data); + $scope.plotOffsetHorizon(data); + $scope.plotWaveletHorizon(data); + var maxAll = $scope.getMaxAll(); + + if(maxAll > 1){ + $scope.updateAmpPlots(maxAll); + } + }; + + $scope.makeEarthModelStruct = function(){ + var image = $scope.curImage; + var mapping = {}; + for(var i=0; i < image.rocks.length; i++){ + mapping[image.colours[i]] = image.rocks[i]; + } + + var data = { + image: $scope.curImage.image, + mapping: mapping, + image_key: $scope.curImage.key, + zrange: $scope.zRange, + domain: $scope.zAxisDomain, + theta: $scope.theta, + name: $scope.earthModelName + }; + + return(data); + }; + + $scope.saveModel = function(){ + var data = $scope.makeEarthModelStruct(); + + if($scope.earthModelName === '' || $scope.earthModelName === undefined){ + var myAlert = $alert({ + title: 'Alert:', + content: 'Model name is required.', + placement: 'alert-success', + type: 'danger', + duration: 5, + show: true + }); + return; + } + + $http.post('/earth_model', data) + .then(function(response){ + $scope.curImage.earth_models.push(response.data); + $scope.zAxisDomain = 'depth'; + $scope.zRange = 1000; + + var myAlert = $alert({ + title: 'Success:', + content: 'You have saved your model \'' + $scope.earthModelName + '\'.', + placement: 'alert-success', + type: 'success', + duration: 5, + show: true + }); + $scope.earthModelName = ""; + }); + }; + + $scope.setDefaults(); + $scope.fetchImageModels(); + $scope.fetchRocks(); + $( document ).ready(function(){ + $scope.setColorPickers(); + }); +}); + +// <-- HELPER FUNCTIONS --> // +// Take two numbers and return the abs max of the two +function getMax(a, b){ + if(Math.abs(a) > Math.abs(b)){ + return Math.abs(a); + } else { + return Math.abs(b); + } +} + +// Get a row from a columnar matrix +function getCrossSection(matrix, rowIndex){ + var arr = []; + for(var i = 0; i < matrix.length; i++){ + arr.push(matrix[i][rowIndex]); + } + return arr; +} \ No newline at end of file diff --git a/static/js/bower.json b/static/js/bower.json new file mode 100644 index 0000000..94882ad --- /dev/null +++ b/static/js/bower.json @@ -0,0 +1,29 @@ +{ + "name": "modelr", + "version": "0.2.5", + "homepage": "https://github.com/kwinkunks/modelr_app", + "description": "Modelr App", + "authors": [ + "justinkheisler", + "benbougher" + ], + "license": "NA", + "dependencies": { + "angular": "~1.4.5", + "angular-animate": "~1.4.5", + "angular-bootstrap-colorpicker": "~3.0.19", + "angular-flexslider": "~1.3.2", + "angular-motion": "~0.4.2", + "angular-strap": "~2.3.1", + "bootstrap": "~3.3.5", + "bower": "*", + "d3": "~3.5.6", + "flexslider": "~2.5.0", + "install": "~1.0.4", + "jquery": "~2.1.4", + "mjolnic-bootstrap-colorpicker": "~2.3.0" + }, + "devDependencies": { + "angular-bootstrap": "~0.13.3" + } +} diff --git a/static/js/controllers/2DCtrl.js b/static/js/controllers/2DCtrl.js new file mode 100644 index 0000000..b6e4c91 --- /dev/null +++ b/static/js/controllers/2DCtrl.js @@ -0,0 +1,749 @@ + +app.controller('2DCtrl', function ($scope, $http, $alert, $timeout) { + + $scope.setDefaults = function(){ + $scope.zDomain = ['depth','time']; + $scope.zAxisDomain = 'depth'; + $scope.zRange = 1000; + + // TODO update from mouse over on seismic plots + $scope.trace = 1; + $scope.traceStr = "1"; + $scope.offset = 1; + $scope.offsetNum = 3; + $scope.offsetStr = "1"; + $scope.twt = 1; + $scope.twtStr = "1"; + $scope.gain = 1; + $scope.gainStr = "1"; + $scope.maxGain = "10"; + $scope.frequency = 20; + $scope.phase = 180.0; + $scope.phaseStr = "180"; + $scope.snr = 3.0; + $scope.snrStr = "3.0" + $scope.frequencyNum = 20.72; + $scope.colorRange = ['#FF0000', '#FFF', '#0000FF']; + + // TODO get from app before so we get the prod url + $scope.server = 'http://localhost:8081'; + + $scope.theta = [0, 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]; + $scope.popover = { + title: "Models", + content: "Choose a model framework from the carousel below, or use the buttons to the right to upload an image or create a new model with the model builder. then assign the model's rocks and other parameters in the panel to the right." + }; + }; + + $scope.setColorPickers = function(){ + for(var i = 0; i < $scope.colorRange.length; i++){ + var colorp = $('.s-color-' + i).colorpicker() + .on('changeColor.colorpicker', function(event){ + var index = event.currentTarget.attributes["data-index"].value; + $(event.currentTarget).css('background-color', $scope.colorRange[index]); + $scope.colorRange[index] = event.color.toHex(); + console.log($scope.colorRange); + }); + } + }; + + $scope.removeColor = function(index){ + $scope.colorRange.splice(index, 1); + $scope.colorDomain.splice(index, 1); + }; + + $scope.addColor = function(){ + $scope.colorRange.push('#FFF'); + $scope.colorDomain.push(0); + $timeout($scope.setColorPickers, 300); + }; + + $scope.fetchImageModels = function(){ + $http.get('/image_model?all') + .then(function(response) { + $scope.images = response.data; + + if($scope.images.length > 0){ + $scope.curImage = $scope.images[0]; + + + for(var i = 0; i < $scope.images.length; i++){ + $scope.images[i].rocks = []; + for(var j = 0; j < $scope.images[i].colours.length; j++){ + var rand = $scope.rocks[Math.floor(Math.random() * $scope.rocks.length)]; + $scope.images[i].rocks.push(rand); + } + } + } + } + ); + }; + + $scope.fetchRocks = function(){ + $http.get('/rock?all'). + then(function(response) { + $scope.rocks = response.data; + } + ); + }; + + $scope.loadSaved = function(){ + + var array = $.map($scope.savedEarthModel.mapping, function(value, index) { + return [value]; + }); + + $scope.curImage.rocks = []; + for(var i = 0; i < array.length; i++){ + $scope.curImage.rocks.push(array[i].rock); + } + console.log($scope.curImage.rocks); + }; + + $scope.slideClick = function(slider){ + $scope.curImage = $scope.images[slider.element.currentSlide]; + }; + + $scope.update_data = function(){ + var earth_model = $scope.makeEarthModelStruct(); + + var seismic = { + frequency: $scope.frequency, + wavelet: "ricker", + dt: 0.001, + phase: $scope.phase, + snr: $scope.snr + }; + + var data = { + seismic: seismic, + earth_model: earth_model, + trace: $scope.trace, + offset: $scope.offset + }; + + var payload = JSON.stringify(data); + $http.get($scope.server + '/data.json?type=seismic&script=convolution_model.py&payload=' + payload) + .then(function(response){ + console.log(response.data); + $scope.plot(response.data); + $scope.maxTrace = String(response.data.seismic.length - 1); + $scope.maxTWT = String(response.data.seismic[0].length - 1); + $scope.maxOffset = String(response.data.offset_gather.length - 1); + $scope.updateClicked = true; + } + ); + }; + + $scope.changeTraceStr = function(){ + $scope.trace = Number($scope.traceStr); + var arr = [$scope.data.seismic[$scope.trace]]; + $scope.vDLog + .setXMin($scope.trace) + .reDraw( + arr, + [0, $scope.data.seismic.length - 1], + [0, $scope.data.seismic[0].length - 1] + ); + }; + + $scope.changeTWTStr = function(){ + $scope.twt = Number($scope.twtStr); + $scope.updateTWT(); + }; + + $scope.getMaxAll = function(){ + var max = []; + var maxT = getMax(d3.max($scope.aTArr) * $scope.gain, d3.min($scope.aTArr) * $scope.gain); + max.push(maxT); + var maxO = getMax(d3.max($scope.aOArr) * $scope.gain, d3.min($scope.aOArr) * $scope.gain); + max.push(maxO); + var maxF = getMax(d3.max($scope.aFArr) * $scope.gain, d3.min($scope.aFArr) * $scope.gain); + max.push(maxF); + return d3.max(max); + }; + + $scope.updateTWT = function(){ + // Update Horizons + var yScale = $scope.vDPlot.yScale; + $scope.vDHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + yScale = $scope.oGPlot.yScale; + $scope.oGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + yScale = $scope.wGPlot.yScale; + $scope.wGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + + $scope.aTArr = getCrossSection($scope.data.seismic, $scope.twt); + $scope.aOArr = getCrossSection($scope.data.offset_gather, $scope.twt); + $scope.aFArr = getCrossSection($scope.data.wavelet_gather, $scope.twt); + + var maxAll = $scope.getMaxAll(); + + if(maxAll > 1){ + $scope.updateAmpPlots(maxAll); + } + + $scope.aTHor + .setGain($scope.gain) + .reDraw($scope.aTArr); + + $scope.aOHor + .setGain($scope.gain) + .reDraw($scope.aOArr); + + $scope.aFHor + .setGain($scope.gain) + .reDraw($scope.aFArr); + }; + + $scope.updateAmpPlots = function(maxAll){ + $scope.aTPlot + .reDraw( + [0, $scope.data.seismic.length - 1], + [maxAll,-maxAll], + [0, $scope.data.seismic.length - 1], + [maxAll,-maxAll] + ); + $scope.aOPlot + .reDraw( + [0, $scope.data.offset_gather.length - 1], + [maxAll,-maxAll], + [0, $scope.data.offset_gather.length - 1], + [maxAll,-maxAll] + ); + + $scope.aFPlot + .reDraw( + [0, $scope.data.wavelet_gather.length - 1], + [maxAll,-maxAll], + [0, $scope.data.wavelet_gather.length - 1], + [maxAll,-maxAll] + ); + }; + + $scope.changeGainStr = function(){ + $scope.gain = Number($scope.gainStr); + $scope.updateTWT(); + }; + + $scope.changeOffsetStr = function(){ + $scope.offset = Number($scope.offsetStr); + $scope.offsetNum = $scope.offset * 3; + var arr = [$scope.data.offset_gather[$scope.offset]]; + $scope.oGLog + .setXMin($scope.offset) + .reDraw( + arr, + [0, $scope.data.offset_gather.length - 1], + [0, $scope.data.offset_gather[0].length - 1] + ); + }; + + $scope.changeFrequencyStr = function(){ + $scope.frequency = Number($scope.frequencyStr); + $scope.frequencyNum = $scope.data.f[$scope.frequency]; + var arr = [$scope.data.wavelet_gather[$scope.frequency]]; + $scope.wGLog + .setXMin($scope.frequency) + .reDraw( + arr, + [0, $scope.data.wavelet_gather.length - 1], + [0, $scope.data.wavelet_gather[0].length - 1] + ); + }; + + $scope.changePhaseStr = function(){ + $scope.phase = Number($scope.phaseStr); + }; + + $scope.changeNoiseStr = function(){ + $scope.snr = Number($scope.snrStr); + }; + + $scope.plotSeismic = function(data, height, max){ + // Variable Density Plot + var width = $('.vd_plot').width(); + if(!$scope.vDPlot){ + $scope.vDPlot = g3.plot('.vd_plot') + .setHeight(height) + .setXTitle("spatial cross-section") + .setYTitle("time [ms]") + .setWidth(width - 30) + .toggleX2Axis(true) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setY2TickFormat("") + .setDuration(5) + .setMargin(20,10,5,40) + .setXDomain([0, data.seismic.length - 1]) + .setYDomain([0, data.seismic[0].length - 1]) + .setX2Domain([0, data.seismic.length - 1]) + .setY2Domain([0, data.seismic[0].length - 1]) + .draw(); + } else { + $scope.vDPlot.reDraw( + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1], + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1] + ); + } + + // Draw Seismic Image + if(!$scope.seis){ + $scope.seis = g3.seismic($scope.vDPlot, data.seismic) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.seis + .setGain($scope.gain) + .setColor($scope.colorScale) + .reDraw(data.seismic); + } + }; + + $scope.plotOffset = function(data, height, max){ + // Offset Gather Plot + var width = $('.og_plot').width(); + if(!$scope.oGPlot){ + $scope.oGPlot = g3.plot('.og_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setXTitle("angle gather") + .toggleX2Axis(true) + .setX2Ticks(6) + .setXTicks(6) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(20,10,5,30) + .setXDomain([0, data.offset_gather.length - 1]) + .setYDomain([0, data.offset_gather[0].length - 1]) + .setX2Domain([0, data.offset_gather.length - 1]) + .setY2Domain([0, data.offset_gather[0].length - 1]) + .draw(); + } else { + $scope.oGPlot.reDraw( + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1], + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1] + ); + } + + // Draw Offset Gather Image + if(!$scope.og){ + $scope.og = g3.seismic($scope.oGPlot, data.offset_gather) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.og + .setGain($scope.gain) + .setColor($scope.colorScale) + .reDraw(data.offset_gather); + } + }; + + $scope.plotWavelet = function(data, height, max){ + // Wavelet Gather Plot + var width = $('.wg_plot').width(); + if(!$scope.wGPlot){ + $scope.wGPlot = g3.plot('.wg_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setXTitle("wavelet gather") + .toggleX2Axis(true) + .setX2Ticks(6) + .setXTicks(6) + .toggleY2Axis(true) + .setXTickFormat("") + .setX2TickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(20,10,5,20) + .setXDomain([0, data.wavelet_gather.length - 1]) + .setYDomain([0, data.wavelet_gather[0].length - 1]) + .setX2Domain([0, data.wavelet_gather.length - 1]) + .setY2Domain([0, data.wavelet_gather[0].length - 1]) + .draw(); + } else { + $scope.wGPlot.reDraw( + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1], + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1] + ); + } + + // Draw Wavelet Gather Image + if(!$scope.wg){ + $scope.wg = g3.seismic($scope.wGPlot, data.wavelet_gather) + .setMax(max) + .setColor($scope.colorScale) + .setGain($scope.gain) + .draw(); + } else { + $scope.wg + .setColor($scope.colorScale) + .setGain($scope.gain) + .reDraw(data.wavelet_gather); + } + }; + + $scope.plotAmplitudeTrace = function(data, height){ + // Amplitude Trace + var width = $('.at_plot').width(); + + if(!$scope.aTPlot){ + $scope.aTPlot = g3.plot('.at_plot') + .setHeight(height) + .setYTitle("amplitude") + .setDuration(5) + .setWidth(width - 30) + .setYTicks(6) + .setY2Ticks(6) + .setX2Title("trace") + .toggleX2Axis(true) + .toggleY2Axis(true) + .setXTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,40) + .setXDomain([0, $scope.data.seismic.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, $scope.data.seismic.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aTArr = getCrossSection($scope.data.seismic, $scope.twt); + + if(!$scope.aTHor){ + $scope.aTHor = g3.horizon($scope.aTPlot, $scope.aTArr) + .setDuration(5) + .draw(); + } else { + $scope.aTHor.reDraw($scope.aTArr); + } + }; + + $scope.changeColor = function(index){ + console.log($scope.colorDomain); + }; + + $scope.plotAmplitudeOffset = function(data, height){ + // Amplitude Offset Plot + var width = $('.ao_plot').width(); + if(!$scope.aOPlot){ + $scope.aOPlot = g3.plot('.ao_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .toggleX2Axis(true) + .setX2Title("\u03B8" + "\u00B0") + .setY2Ticks(6) + .setYTicks(6) + .setXTicks(6) + .setX2Ticks(6) + .toggleY2Axis(true) + .setX2TickFormat(function(d, i){ + return $scope.data.theta[d]; + }) + .setXTickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,30) + .setXDomain([0, data.offset_gather.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, data.offset_gather.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aOArr = getCrossSection($scope.data.offset_gather, $scope.twt); + + if(!$scope.aOHor){ + $scope.aOHor = g3.horizon($scope.aOPlot, $scope.aOArr) + .setDuration(5) + .draw(); + } else { + $scope.aOHor.reDraw($scope.aOArr); + } + }; + + $scope.plotAmplitudeFreq = function(data, height){ + + // Amplitude Freq Plot + var width = $('.af_plot').width(); + if(!$scope.aFPlot){ + $scope.aFPlot = g3.plot('.af_plot') + .setHeight(height) + .setWidth(width - 30) + .setDuration(5) + .setX2Title("centre frequency Hz") + .toggleX2Axis(true) + .setY2Ticks(6) + .setYTicks(6) + .setXTicks(6) + .setX2Ticks(6) + .toggleY2Axis(true) + .setX2TickFormat(function(d, i){ + return Math.floor($scope.data.f[d]); + }) + .setXTickFormat("") + .setYTickFormat("") + .setY2TickFormat("") + .setMargin(5,10,40,20) + .setXDomain([0, $scope.data.wavelet_gather.length - 1]) + .setYDomain([1, -1]) + .setX2Domain([0, $scope.data.wavelet_gather.length - 1]) + .setY2Domain([1, -1]) + .draw(); + } + + $scope.aFArr = getCrossSection($scope.data.wavelet_gather, $scope.twt); + + if(!$scope.aFHor){ + $scope.aFHor = g3.horizon($scope.aFPlot, $scope.aFArr) + .setDuration(5) + .draw(); + } else { + $scope.aFHor.reDraw($scope.aFArr); + } + }; + + $scope.plotVDWiggle = function(data){ + // Draw VD Plot Wiggle + var arr = [data.seismic[$scope.trace]]; + if(!$scope.vDLog){ + $scope.vDLog = g3.wiggle($scope.vDPlot, arr) + .setXMin($scope.trace) + .setGain(80) + .setDuration(5) + .draw(); + } else { + $scope.vDLog + .setXMin($scope.trace) + .reDraw( + arr, + [0, data.seismic.length - 1], + [0, data.seismic[0].length - 1] + ); + } + }; + + $scope.plotVDHorizon = function(data){ + var xScale = $scope.vDPlot.xScale; + var yScale = $scope.vDPlot.yScale; + if(!$scope.vDHor){ + $scope.vDHor = $scope.vDPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.seismic.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.vDHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plotOffsetWiggle = function(data){ + // Draw Offset Wiggle + var arr = [data.offset_gather[$scope.offset]]; + if(!$scope.oGLog){ + $scope.oGLog = g3.wiggle($scope.oGPlot, arr) + .setXMin($scope.offset) + .setGain(5) + .setDuration(5) + .draw(); + } else { + $scope.oGLog + .setXMin($scope.offset) + .reDraw( + arr, + [0, data.offset_gather.length - 1], + [0, data.offset_gather[0].length - 1] + ); + } + }; + + $scope.plotWaveletWiggle = function(data){ + // Draw Offset Wiggle + var arr = [data.wavelet_gather[$scope.frequency]]; + if(!$scope.wGLog){ + $scope.wGLog = g3.wiggle($scope.wGPlot, arr) + .setXMin($scope.frequency) + .setGain(22.7) + .setDuration(5) + .draw(); + } else { + $scope.wGLog + .setXMin($scope.frequency) + .reDraw( + arr, + [0, data.wavelet_gather.length - 1], + [0, data.wavelet_gather[0].length - 1] + ); + } + }; + + $scope.plotOffsetHorizon = function(data){ + // Draw Offset Horizon + var xScale = $scope.oGPlot.xScale; + var yScale = $scope.oGPlot.yScale; + if(!$scope.oGHor){ + $scope.oGHor = $scope.oGPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.offset_gather.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.oGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plotWaveletHorizon = function(data){ + // Draw Wavelet Horizon + var xScale = $scope.wGPlot.xScale; + var yScale = $scope.wGPlot.yScale; + if(!$scope.wGHor){ + $scope.wGHor = $scope.wGPlot.svg.append("line") + .style("stroke", "green") + .style("opacity", 1.5) + .attr("x1", xScale(0)) + .attr("y1", yScale($scope.twt)) + .attr("x2", xScale(data.wavelet_gather.length - 1)) + .attr("y2", yScale($scope.twt)); + } else { + $scope.wGHor + .transition() + .duration(5) + .attr("y1", yScale($scope.twt)) + .attr("y2", yScale($scope.twt)); + } + }; + + $scope.plot = function(data){ + $scope.data = data; + var height = 450; + var max = getMax(data.max, data.min); + max = Number(max.toFixed(2)); + + if(!$scope.colorDomain){ + $scope.colorDomain = [-max, 0, max]; + } + + $scope.colorScale = d3.scale.linear().domain($scope.colorDomain).range($scope.colorRange); + $scope.plotSeismic(data, height, max); + $scope.plotOffset(data, height, max); + $scope.plotWavelet(data, height, max); + + height = 150; + $scope.plotAmplitudeTrace(data, height); + $scope.plotAmplitudeOffset(data, height); + $scope.plotAmplitudeFreq(data, height); + $scope.plotVDWiggle(data); + $scope.plotOffsetWiggle(data); + $scope.plotWaveletWiggle(data, height); + $scope.plotVDHorizon(data); + $scope.plotOffsetHorizon(data); + $scope.plotWaveletHorizon(data); + var maxAll = $scope.getMaxAll(); + + if(maxAll > 1){ + $scope.updateAmpPlots(maxAll); + } + }; + + $scope.makeEarthModelStruct = function(){ + var image = $scope.curImage; + var mapping = {}; + for(var i=0; i < image.rocks.length; i++){ + mapping[image.colours[i]] = image.rocks[i]; + } + + var data = { + image: $scope.curImage.image, + mapping: mapping, + image_key: $scope.curImage.key, + zrange: $scope.zRange, + domain: $scope.zAxisDomain, + theta: $scope.theta, + name: $scope.earthModelName + }; + + return(data); + }; + + $scope.saveModel = function(){ + var data = $scope.makeEarthModelStruct(); + + if($scope.earthModelName === '' || $scope.earthModelName === undefined){ + var myAlert = $alert({ + title: 'Alert:', + content: 'Model name is required.', + placement: 'alert-success', + type: 'danger', + duration: 5, + show: true + }); + return; + } + + $http.post('/earth_model', data) + .then(function(response){ + $scope.curImage.earth_models.push(response.data); + $scope.zAxisDomain = 'depth'; + $scope.zRange = 1000; + + var myAlert = $alert({ + title: 'Success:', + content: 'You have saved your model \'' + $scope.earthModelName + '\'.', + placement: 'alert-success', + type: 'success', + duration: 5, + show: true + }); + $scope.earthModelName = ""; + }); + }; + + $scope.setDefaults(); + $scope.fetchImageModels(); + $scope.fetchRocks(); + $( document ).ready(function(){ + $scope.setColorPickers(); + }); +}); diff --git a/static/js/controllers/helperFuctions.js b/static/js/controllers/helperFuctions.js new file mode 100644 index 0000000..37220ba --- /dev/null +++ b/static/js/controllers/helperFuctions.js @@ -0,0 +1,18 @@ +// <-- HELPER FUNCTIONS --> // +// Take two numbers and return the abs max of the two +function getMax(a, b){ + if(Math.abs(a) > Math.abs(b)){ + return Math.abs(a); + } else { + return Math.abs(b); + } +} + +// Get a row from a columnar matrix +function getCrossSection(matrix, rowIndex){ + var arr = []; + for(var i = 0; i < matrix.length; i++){ + arr.push(matrix[i][rowIndex]); + } + return arr; +} \ No newline at end of file diff --git a/static/js/controllers/start.js b/static/js/controllers/start.js new file mode 100644 index 0000000..dba2adb --- /dev/null +++ b/static/js/controllers/start.js @@ -0,0 +1,11 @@ +'use strict'; +var app = angular.module('modelr', + ['mgcrea.ngStrap', + 'ngAnimate', + 'angular-flexslider']); + + +app.config(['$interpolateProvider', function($interpolateProvider) { + $interpolateProvider.startSymbol('{['); + $interpolateProvider.endSymbol(']}'); +}]); diff --git a/static/js/g3.js b/static/js/g3.js index a765f73..8cc9aec 100644 --- a/static/js/g3.js +++ b/static/js/g3.js @@ -1,4 +1,4 @@ -/*! g3 - v0.0.1 - 2015-09-08 - justinkheisler */ +/*! g3 - v0.0.1 - 2015-09-17 - justinkheisler */ 'use strict'; ;(function (window) { @@ -21,7 +21,7 @@ function createAxis(scale, innerTickSize, orient, ticks){ .orient(orient) .ticks(ticks); } -g3.horizon = function(options, plot, data){ +g3.horizon = function(plot, data, options){ if(!data || !$.isArray(data)){ return 'Param: data is missing, An array required'; } if(!plot){ return 'Param: plot is missing, a div to attach the svg is required'; } @@ -32,6 +32,8 @@ g3.horizon = function(options, plot, data){ horizon.xMin = plot.xDomain[0]; horizon.yInt = 1; horizon.yMin = plot.yDomain[0]; + horizon.duration = 500; + horizon.gain = 1; if(options){ if(options.interpolate){ horizon.interpolate = options.interpolate; } @@ -51,27 +53,46 @@ g3.horizon = function(options, plot, data){ return this; } + horizon.setDuration = function(duration){ + this.duration = duration; + return this; + } + + horizon.setGain = function(gain){ + this.gain = gain; + return this; + } + horizon.draw = function(){ var lineFunc = d3.svg.line() .x(function (d, i) { return plot.xScale(i + horizon.xMin); }) .y(function (d) { - return plot.yScale(d / 1000); + return plot.yScale(d * horizon.gain); }) .interpolate(this.interpolate); this.svg = plot.svg.append('svg:path') .attr('d', lineFunc(data)) - .attr('stroke', 'blue') - .attr('stroke-width', 2) + .attr('stroke', 'green') + .attr('stroke-width', 1.5) .attr('fill', 'none'); return this; } - horizon.reDraw = function(){ + horizon.reDraw = function(data){ + var lineFunc = d3.svg.line() + .x(function (d, i) { + return plot.xScale(i + horizon.xMin); + }) + .y(function (d) { + return plot.yScale(d * horizon.gain); + }) + .interpolate(this.interpolate); + this.svg.transition() - .duration(500) + .duration(this.duration) .attr('d', lineFunc(data)); return this; } @@ -90,6 +111,7 @@ g3.log = function(plot, data, options){ log.yInt = 1; log.yMin = plot.yDomain[0]; log.color = "blue"; + log.duration = 500; if(options){ if(options.yInt){ log.yInt = options.zInt; } @@ -99,6 +121,11 @@ g3.log = function(plot, data, options){ } // Setters + log.setDuration = function(duration){ + this.duration = duration; + return this; + } + log.setYInt = function(yInt){ this.yInt = yInt; return this; @@ -184,14 +211,11 @@ g3.log = function(plot, data, options){ log.reDraw = function(data){ this.svg.transition() - .duration(500) + .duration(this.duration) .attr('d', lineFunc(data)) .ease('linear'); - return this; } - - return log; } @@ -214,6 +238,7 @@ g3.plot = function(elem, options){ plot.x2Orient = 'bottom'; plot.yOrient = 'left'; plot.y2Orient = 'right'; + plot.duration = 500; if(options){ if(options.margin){ plot.margin = options.margin; } @@ -223,6 +248,11 @@ g3.plot = function(elem, options){ if(options.yDomain){ plot.yDomain = options.yDomain; } } + plot.setDuration = function(duration){ + this.duration = duration; + return this; + } + plot.setMargin = function(top, right, bottom, left){ this.margin = {top: top, right: right, bottom: bottom, left: left}; return this; @@ -289,12 +319,12 @@ g3.plot = function(elem, options){ } plot.setX2Ticks = function(ticks){ - this.xTicks = ticks; + this.x2Ticks = ticks; return this; } plot.setY2Ticks = function(ticks){ - this.yTicks = ticks; + this.y2Ticks = ticks; return this; } @@ -366,6 +396,9 @@ g3.plot = function(elem, options){ .domain(this.yDomain) .range([0, this.height]); + var innerWidth = this.width - this.margin.left - this.margin.right, + innerHeight = this.height - this.margin.top - this.margin.bottom; + // Append svg object to dom element this.svg = d3.select(elem).append('svg') .attr('class', 'log_plot') @@ -381,10 +414,7 @@ g3.plot = function(elem, options){ this.xAxis.tickFormat(this.xTickFormat); this.svg.append('g') .attr('class', 'x axis') - .call(this.xAxis) - .selectAll("text") - .style("text-anchor", "start") - .attr("transform", "rotate(-45)" ); + .call(this.xAxis); } if(this.yAxisVisible){ this.yAxis = createAxis(this.yScale, -this.width, this.yOrient, this.yTicks); @@ -402,10 +432,7 @@ g3.plot = function(elem, options){ this.svg.append('g') .attr('class', 'x2 axis') .attr("transform", "translate(" + "0," + this.height + ")") - .call(this.x2Axis) - .selectAll("text") - .style("text-anchor", "start") - .attr("transform", "rotate(45)" ); + .call(this.x2Axis); } if(this.y2AxisVisible){ this.y2Scale = d3.scale.linear() @@ -419,22 +446,67 @@ g3.plot = function(elem, options){ .call(this.y2Axis); } - if(this.xTitle){ - this.svg.append("text") - .attr("x", (this.width) / 2) - .attr("y", -30) - .style("text-anchor", "middle") - .text(this.xTitle); - } - if(this.yTitle){ - this.svg.append("text") - .attr("transform", "rotate(-90)") - //.attr("x", -15) - .attr("y", -20) - .attr("dy", "1em") - .style("text-anchor", "end") - .text(this.yTitle); - } + if(this.xTitle){ + + if(this.xTickFormat === ""){ + var margin = -10; + } else { + var margin = -30; + } + this.svg.append("text") + .attr("x", (this.width) / 2) + .attr("y", margin) + .style("text-anchor", "middle") + .style("font-size", 12) + .text(this.xTitle); + } + if(this.yTitle){ + + if(this.yTickFormat === ""){ + var yMargin = -10; + } else { + var yMargin = -40; + } + + this.svg.append("text") + .attr("transform", "rotate(-90)") + .attr("y", yMargin) + .attr("dy", "1em") + .style("text-anchor", "end") + .style("font-size", 12) + .text(this.yTitle); + } + if(this.x2Title){ + + if(this.x2TickFormat === ""){ + var margin = 10; + } else { + var margin = 30; + } + this.svg.append("text") + .attr("transform", "translate(" + "0," + this.height + ")") + .attr("x", (this.width) / 2) + .attr("y", margin) + .style("text-anchor", "middle") + .style("font-size", 12) + .text(this.x2Title); + } + if(this.y2Title){ + + if(this.yTickFormat === ""){ + var yMargin = -10; + } else { + var yMargin = -40; + } + + this.svg.append("text") + .attr("transform", "translate(" + "0," + this.height + ")") + .attr("y", yMargin) + .attr("dy", "1em") + .style("text-anchor", "end") + .style("font-size", 12) + .text(this.y2Title); + } return this; }; @@ -443,19 +515,16 @@ g3.plot = function(elem, options){ this.xScale.domain(xDomain); this.svg.select('.x.axis') .transition() - .duration(500) + .duration(this.duration) .call(this.xAxis) - .ease('linear') - .selectAll("text") - .style("text-anchor", "start") - .attr("transform", "rotate(-45)"); + .ease('linear'); } if(yDomain){ this.yScale.domain(yDomain); this.svg.select('.y.axis') .transition() - .duration(500) + .duration(this.duration) .call(this.yAxis) .ease('linear'); } @@ -464,19 +533,16 @@ g3.plot = function(elem, options){ this.x2Scale.domain(x2Domain); this.svg.select('.x2.axis') .transition() - .duration(500) + .duration(this.duration) .call(this.x2Axis) - .ease('linear') - .selectAll("text") - .style("text-anchor", "start") - .attr("transform", "rotate(-45)"); + .ease('linear'); } if(y2Domain){ this.y2Scale.domain(y2Domain); this.svg.select('.y2.axis') .transition() - .duration(500) + .duration(this.duration) .call(this.y2Axis) .ease('linear'); } @@ -491,11 +557,17 @@ g3.seismic = function(plot, data, options){ var seismic = {}; seismic.max = 1; seismic.gain = 1; + seismic.plot = plot; + seismic.data = data; + seismic.duration = 500; seismic.draw = function(){ - seismic.color = d3.scale.linear() + + if(!seismic.color){ + seismic.color = d3.scale.linear() .domain([-this.max, 0, this.max]) .range(['#FF0000', '#FFF', '#0000FF']); + } var elem = $(plot.elem); this.canvas = d3.select(plot.elem) @@ -511,22 +583,65 @@ g3.seismic = function(plot, data, options){ return this; } - seismic.drawImage = function(canvas){ - var context = canvas.node().getContext('2d'), - x = data.length, - y = data[0].length, - image = context.createImageData(x,y); + seismic.reDraw = function(data){ + + // Update the Canvas Attributes + seismic.canvas + .attr('width', data.length) + .attr('height', data[0].length) + .style('width', seismic.plot.width + 'px') + .style('height', seismic.plot.height + 'px'); + // Wipe the old canvas, the new size can be different + seismic.context.clearRect(0, 0, seismic.data.length, seismic.data[0].length); + + seismic.data = data; + var x = data.length, + y = data[0].length; + var image = seismic.context.createImageData(x,y); + + // Paint the image for(var i = 0, p = -1; i < y; ++ i){ for(var j = 0; j < x; ++j){ - var c = d3.rgb(seismic.color(data[j][i])); + var c = d3.rgb(seismic.color(data[j][i] * seismic.gain)); image.data[++p] = c.r; image.data[++p] = c.g; image.data[++p] = c.b; image.data[++p] = 255; } } - context.putImageData(image, 0, 0); + + // Dump image to canvas + seismic.context.putImageData(image, 0, 0); + return this; + } + + seismic.drawImage = function(canvas){ + seismic.context = canvas.node().getContext('2d'); + var x = seismic.data.length, + y = seismic.data[0].length; + seismic.image = seismic.context.createImageData(x,y); + + for(var i = 0, p = -1; i < y; ++ i){ + for(var j = 0; j < x; ++j){ + var c = d3.rgb(seismic.color(seismic.data[j][i] * seismic.gain)); + seismic.image.data[++p] = c.r; + seismic.image.data[++p] = c.g; + seismic.image.data[++p] = c.b; + seismic.image.data[++p] = 255; + } + } + seismic.context.putImageData(seismic.image, 0, 0); + return this; + } + + seismic.setColor = function(colorScale){ + this.color = colorScale; + return this; + } + + seismic.setDuration = function(duration){ + this.duration = duration; return this; } @@ -554,7 +669,9 @@ g3.wiggle = function(plot, data, options){ wiggle.xMin = plot.xDomain[0]; wiggle.yInt = 1; wiggle.yMin = plot.yDomain[0]; + wiggle.rand = Math.floor((Math.random() * 100) + 1); wiggle.sampleRate = 1; + wiggle.duration = 500; if(options){ if(options.skip){ wiggle.skip = options.skip; } @@ -623,6 +740,11 @@ g3.wiggle = function(plot, data, options){ return this; } + wiggle.setDuration = function(duration){ + this.duration = duration; + return this; + } + wiggle.draw = function() { for(var k = data.length - 1; k >= 0; k--){ if(this.skip === 0 || k % this.skip === 0){ @@ -652,20 +774,21 @@ g3.wiggle = function(plot, data, options){ .attr('class', 'line' + k) .attr('d', line(data[k])) .attr('stroke', 'black') - .attr('stroke-width', 0.25) + .attr('stroke-width', 0.50) .attr('fill', 'none'); plot.svg.datum(data[k]); plot.svg.append('clipPath') - .attr('id', 'clip-below' + k) + .attr('id', 'clip-below' + wiggle.rand + k) .append('path') .attr('d', area.x0(plot.width)); plot.svg.append('path') .attr('id', 'area-below' + k) - .attr('clip-path', 'url(#clip-below' + k) - .attr('fill', 'grey') + .attr('clip-path', 'url(#clip-below' + wiggle.rand + k) + .attr('fill', 'black') + .style('opacity', 0.4) .attr('d', area.x0(function (d, i){ return plot.xScale(d * wiggle.gain + wiggle.xMin + k * wiggle.sampleRate); })); @@ -682,7 +805,7 @@ wiggle.reDraw = function(data, xDomain, yDomain){ plot.svg.select('.x.axis') .transition() - .duration(500) + .duration(this.duration) .call(plot.xAxis) .selectAll("text") .style("text-anchor", "start") @@ -690,13 +813,16 @@ wiggle.reDraw = function(data, xDomain, yDomain){ plot.svg.select('.y.axis') .transition() - .duration(500) + .duration(this.duration) .call(plot.yAxis); for(var k = data.length - 1; k >= 0; k--){ if(this.skip === 0 || k % this.skip === 0){ var mean = d3.mean(data[k]); + plot.svg.select("#clip-below" + wiggle.rand + k) + .remove() + // Line function var line = d3.svg.area() .interpolate('basis') @@ -719,21 +845,21 @@ wiggle.reDraw = function(data, xDomain, yDomain){ plot.svg.select(".line" + k) .transition() - .duration(500) + .duration(this.duration) .attr('d', line(data[k])) .ease("linear"); plot.svg.datum(data[k]); - plot.svg.select("#clip-below" + k) - .transition() - .duration(500) - .attr('d', area.x0(plot.width)) - .ease('linear'); + plot.svg.append('clipPath') + .attr('id', 'clip-below' + wiggle.rand + k) + .append('path') + .attr('d', area.x0(plot.width)); plot.svg.select("#area-below" + k) + .attr('clip-path', 'url(#clip-below' + wiggle.rand + k) .transition() - .duration(500) + .duration(this.duration) .attr('d', area.x0(function (d, i){ return plot.xScale(d * wiggle.gain + wiggle.xMin + k * wiggle.sampleRate); })) diff --git a/static/js/gulpfile.js b/static/js/gulpfile.js new file mode 100644 index 0000000..691262e --- /dev/null +++ b/static/js/gulpfile.js @@ -0,0 +1,27 @@ +var gulp = require('gulp'); +var jshint = require('gulp-jshint'); +var concat = require('gulp-concat'); +var watch = require('gulp-watch'); + +gulp.task('default', ['jshint', 'concat']); + +var js; +gulp.task('concat', function (cb) { + var options = {}; + watch('controllers/*.js', options, function (e) { + console.log('e:'+JSON.stringify(e)); + console.log('\n'); + gulp.src(['controllers/start.js','controllers/!(start)*.js',]) + .pipe(concat('app.js', {newLine: '\n'})) + .pipe(gulp.dest('./')); + }); +}); + +// Lint JavaScript +gulp.task('jshint', function () { + return gulp.src([ + 'controllers/*.js' + ]) + .pipe(jshint()) + .pipe(jshint.reporter('jshint-stylish')); +}); \ No newline at end of file diff --git a/static/js/package.json b/static/js/package.json new file mode 100644 index 0000000..54acd3e --- /dev/null +++ b/static/js/package.json @@ -0,0 +1,29 @@ +{ + "name": "modelr", + "version": "1.0.0", + "description": "Modelr App", + "main": "app.yaml", + "dependencies": { + "gulp": "^3.9.0" + }, + "devDependencies": { + "gulp": "^3.9.0", + "gulp-concat": "^2.6.0", + "gulp-jshint": "^1.11.2", + "gulp-watch": "^4.3.5", + "jshint-stylish": "^2.0.1" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "justinkheisler, benbougher", + "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/kwinkunks/modelr_app.git" + }, + "bugs": { + "url": "https://github.com/kwinkunks/modelr_app/issues" + }, + "homepage": "https://github.com/kwinkunks/modelr_app#readme" +} diff --git a/templates/1D_model.html b/templates/1D_model.html index f243ee5..cb79bf1 100644 --- a/templates/1D_model.html +++ b/templates/1D_model.html @@ -7,15 +7,12 @@ tspan { font-size: 9px; } - - table{ table-layout: fixed; margin-left: 20px; margin-right: 20px; } - model-plot { font: 16px sans-serif; } @@ -27,15 +24,12 @@ .y-label { font: 16px sans-serif; } - .menu-label { font: 12px sans-serif; } - .intervalRow { vertical-align: middle; } - .wiggle-fill { fill: grey; stroke-width: 0; @@ -46,24 +40,19 @@ stroke-width: 1; stroke: black; } - - .deleteBtn{ margin: 10px 0 0 0; vertical-align: top; color:red; } - .menuSelect{ vertical-align: top; margin: 6px 0 0 0; } - .cblock { font-size: 200%; margin-right: 8px; } - .domain { fill:none; stroke: #000; @@ -85,7 +74,6 @@ var db_rocks = {{db_rocks}}; var colour_map = {{colour_map}}; var db_fluids = {{db_fluids}}; - setup1D("#rock-model-container",db_rocks, db_fluids, colour_map, 10000, "#rock-menu", "#plot-container", "#reflectivity-menu", "#seismic-menu","{{HOSTNAME}}"); @@ -114,19 +102,18 @@