diff --git a/coffee/d3layer.coffee b/coffee/d3layer.coffee index 8eba50e..9a23a92 100644 --- a/coffee/d3layer.coffee +++ b/coffee/d3layer.coffee @@ -2,7 +2,7 @@ # provides a layer of circles corresponding to the area of influence of an eruption # uses d3 to draw on a mapbox layer. -@etna.d3layer = (map, mapDrawSvg, mapDrawGroup, lavaDrawGroup) -> +@etna.d3layer = (map, mapDrawSvg, mapDrawGroup) -> etnaLocation = [15.004, 37.734] # official plume area for different VEI values (from Wikipedia) plumes = { @@ -40,30 +40,6 @@ firstDraw = false pixelLocation = project(etnaLocation) - # mapDrawGroup.selectAll('rect') - # .attr('x', pixelLocation[0]) - # .attr('y', pixelLocation[1]) - # .attr('width', 5) - # .attr('height', (d, i) -> - # h = 0 - # if d.lavaflows - # for direction, length of d.lavaflows - # #h = 1 - # switch direction - # when "South" then h = length - - # h - # #dist = d.lava / 111 - # ) - - # lineFunction = d3.svg.line - # .x( (d) -> - # d.x - # ) - # .y( (d) -> - # d.y - # ) - # .interpolate("linear") d3.selectAll('circle') .attr('cx', pixelLocation[0]) @@ -75,59 +51,6 @@ pixelLocation[1] - distPoint[1] ) - d3.selectAll('path') - .attr('d', (d, i) -> - #lineFunction(d) - path = "M 0 0 L 0 0" - if d.lavaflows && d.lavaflows - for direction, length of d.lavaflows - switch direction - when "South" - dist = length / 111 - distPointLat = etnaLocation[1] - dist - distPoint = project([etnaLocation[0], distPointLat]) - path = "M #{pixelLocation[0]} #{pixelLocation[1]} L #{distPoint[0]} #{distPoint[1]}" - when "North" - dist = length / 111 - distPointLat = etnaLocation[1] + dist - distPoint = project([etnaLocation[0], distPointLat]) - path = "M #{pixelLocation[0]} #{pixelLocation[1]} L #{distPoint[0]} #{distPoint[1]}" - when "East" - dist = length / (111 * Math.cos(pixelLocation[1])) - distPointLon = etnaLocation[0] + dist - distPoint = project([distPointLon, etnaLocation[1]]) - path = "M #{pixelLocation[0]} #{pixelLocation[1]} L #{distPoint[0]} #{distPoint[1]}" - when "West" - dist = length / (111 * Math.cos(pixelLocation[1])) - distPointLon = etnaLocation[0] - dist - distPoint = project([distPointLon, etnaLocation[1]]) - path = "M #{pixelLocation[0]} #{pixelLocation[1]} L #{distPoint[0]} #{distPoint[1]}" - else - path = "M 0 0 L 0 0" - - path - ) - .attr("stroke", "red") - .attr("stroke-width", 10) - .attr("fill", "red") - .selectAll('title') - .text((d, i) -> - text = "lavaflow in #{d.date.getFullYear()}" - # if d.lavaflows && d.lavaflows - # for direction, length of d.lavaflows - # text = "#{text} #{length} in direction #{direction}" - text - ) - - # .attr('x1', pixelLocation[0]) - # .attr('x2', pixelLocation[0] + 1) - # .attr('y1', pixelLocation[1]) - # .attr('y2', (d, i) -> - # if d.lavaflows - # console.log d - # pixelLocation[1] + 1 - # ) - # binds the bounding box and eruptions data to the svg data: (boundingBox, eruptions) -> @@ -136,10 +59,7 @@ .data(eruptions) feature.exit().remove() feature.enter().append('circle') - lava = lavaDrawGroup.selectAll('path') - .data(eruptions) - lava.exit().remove() - lava.enter().append('path').append('title') + # sets the visible map extension extent: () -> diff --git a/coffee/eruptions.coffee b/coffee/eruptions.coffee index b91f76b..2561611 100644 --- a/coffee/eruptions.coffee +++ b/coffee/eruptions.coffee @@ -78,7 +78,7 @@ if location markerLayer.add_feature geometry: - coordinates: [location.lon-0.01, location.lat-0.01] # sightly offset for overlaying pins + coordinates: [location.lon-0.005, location.lat-0.005] # sightly offset for overlaying pins properties: 'marker-color': '#777' 'marker-size': 'small' @@ -110,7 +110,7 @@ if location markerLayer.add_feature geometry: - coordinates: [location.lon+0.01, location.lat+0.01] # slightly offset for overlaying pins + coordinates: [location.lon+0.005, location.lat+0.005] # slightly offset for overlaying pins properties: 'marker-color': '#000' 'marker-size': 'small' @@ -127,7 +127,7 @@ if location markerLayer.add_feature geometry: - coordinates: [location.lon-0.01, location.lat+0.01] # slightly offset for overlaying pins + coordinates: [location.lon-0.005, location.lat+0.005] # slightly offset for overlaying pins properties: 'marker-color': '#5C461F' 'marker-size': 'small' @@ -139,9 +139,10 @@ # Module Singleton # ================================ # init the non-changing data for this singleton - init: (eruptionData, map, circleLayer, markerLayer) => + init: (eruptionData, map, circleLayer, markerLayer, lavaLayer) => @map = map @circleLayer = circleLayer + @lavaLayer = lavaLayer @markerLayer = markerLayer @markerLayer.factory((f) -> elem = mapbox.markers.simplestyle_factory(f) @@ -149,6 +150,7 @@ ) @interaction = mapbox.markers.interaction(@markerLayer) @sanitizedData = [] + @lavaFlows = [] for eruption of eruptionData @sanitizedData.push 'date': parseDate(eruption) @@ -160,6 +162,15 @@ 'earthquake': eruptionData[eruption].earthquake 'lavaflows': eruptionData[eruption].lavaflows + for lavaStream in eruptionData[eruption].lavaflows + @lavaFlows.push + 'direction': lavaStream.flow.direction + 'length': lavaStream.flow.length + 'crater': lavaStream.crater + 'date': parseDate(eruption) + 'reachedHere': 1 + 'yearsReached': [parseDate(eruption).getFullYear()] + drawBarchart: (eruptionData) => svg_container = d3.select("#map-legend").append("svg") @@ -230,8 +241,13 @@ dataFiltered = @sanitizedData.filter( (d, i) -> true if (d.date >= focusScale.domain()[0]) && (d.date <= focusScale.domain()[1]) ) - @circleLayer.data(boundingBox, dataFiltered); + lavaFiltered = @lavaFlows.filter( (d, i) -> + true if (d.date >= focusScale.domain()[0]) && (d.date <= focusScale.domain()[1]) + ) + @circleLayer.data(boundingBox, dataFiltered) @circleLayer.draw(); + @lavaLayer.data(boundingBox, lavaFiltered) + @lavaLayer.draw() @map.refresh(); @@ -277,5 +293,4 @@ focusScale.domain()[1].getFullYear()]) # make sure z-index of markers is good - #$(".simplestyle-marker:not('.town-marker')").parent('div').addClass('markers') $(".simplestyle-marker").parent('div').addClass('markers') \ No newline at end of file diff --git a/coffee/lavaLayer.coffee b/coffee/lavaLayer.coffee new file mode 100644 index 0000000..7fb68ab --- /dev/null +++ b/coffee/lavaLayer.coffee @@ -0,0 +1,126 @@ +@etna = @etna || {} + +@etna.lavaLayer = (map, mapDrawSvg, mapDrawGroup) -> + + etnaLocation = [15.004, 37.734] + # TODO: these should be shared with the eruptions layer + craterLocations = { + "NorthEast": [15.0636, 37.7516], + "SouthEast": [15.0742, 37.7098], + "Voragine": [15.0677, 37.7305], + "Bocca Nuova": [15.0197, 37.7256] + } + + firstDraw = true + + + # projects a location in geographical data to pixel-space. + project = (location) -> + point = map.locationPoint + lat: location[1] + lon: location[0] + [point.x, point.y] + + + # calculates the distance in lat points with the hack 111km = 1 lat point + getLatDistance = (distance) -> + distance / 111; + + + getLonDistance = (distance, latitude) -> + distance / (111 * Math.cos(latitude)) + + + getPath = (length, craterLocation, craterLocationMapped, angle, horizDir, vertDir) -> + distLon = getLonDistance(length * Math.sin(angle), craterLocation[1]) + distLat = getLatDistance(length * Math.cos(angle)) + distPointLon = craterLocation[0] + (horizDir * distLon) + distPointLat = craterLocation[1] + (vertDir * distLat) + + distPoint = project([distPointLon, distPointLat]) + path = "M #{craterLocationMapped[0]} #{craterLocationMapped[1]} L #{distPoint[0]} #{distPoint[1]}" + + + draw: () -> + if firstDraw + mapDrawSvg + .attr('width', map.dimensions.x) + .attr('height', map.dimensions.y) + .style('margin-left', '0px') + .style('margin-top', '0px') + firstDraw = false + + pixelLocation = project(etnaLocation) + + mapDrawGroup.selectAll('path') + .attr('d', (d, i) -> + # TODO how is this possible? + if d == 0 + return "M 0 0 L 0 0" + craterLocation = craterLocations[d.crater] + if !craterLocation # TODO: should not be possible + return "M 0 0 L 0 0" + craterLocationMapped = project(craterLocation) + + # ANGLES: + # South, North: 0 + # -> steered over vert direction + # East, West: PI / 2 + # -> steered over horiz direction + + switch d.direction + when "South" + getPath(d.length, craterLocation, craterLocationMapped, 0, 1, -1) + when "North" + getPath(d.length, craterLocation, craterLocationMapped, 0, -1, 1) + when "East" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 2, 1, 1) + when "West" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 2, -1, 1) + when "SouthEast" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 4, 1, -1) + when "SouthWest" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 4, -1, -1) + when "NorthWest" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 4, -1, 1) + when "NorthEast" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 4, 1, 1) + when "NNorthWest" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 8, -1, 1) + when "WNorthWest" + getPath(d.length, craterLocation, craterLocationMapped, 3 * (Math.PI / 8), -1, 1) + when "NNorthEast" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 8, 1, 1) + when "WSouthWest" + getPath(d.length, craterLocation, craterLocationMapped, 3 * (Math.PI / 8), -1, -1) + when "SSouthEast" + getPath(d.length, craterLocation, craterLocationMapped, Math.PI / 8, 1, -1) + when "ESouthEast" + getPath(d.length, craterLocation, craterLocationMapped, 3*(Math.PI / 8), 1, -1) + else + "M 0 0 L 0 0" + + ) + .attr("stroke", "red") + .attr("stroke-width", 10) + .attr("fill", "red") + .selectAll('title') + .text((d, i) -> + "lavaflow in #{d.date.getFullYear()}" + ) + + + data: (boundingBox, flows) -> + bounds = d3.geo.bounds(boundingBox) + lava = mapDrawGroup.selectAll('path') + .data(flows) + lava.exit().remove().remove() + lava.enter().append('path').append('title') + + + # sets the visible map extension + extent: () -> + new MM.Extent( + new MM.Location(bounds[0][1], bounds[0][0]), + new MM.Location(bounds[1][1], bounds[1][0]) + ) diff --git a/coffee/map.coffee b/coffee/map.coffee index b5418d3..347ca9d 100644 --- a/coffee/map.coffee +++ b/coffee/map.coffee @@ -54,25 +54,37 @@ # init the layer on which the eruption circles will be drawn @initD3Layer() + # init the layer on which the lava flows will be drawn + @initLavaLayer() + # init and draw the barchart - etna.eruptionsChart.init(etna.eruptions, @map, @circleLayer, @markerLayer) + etna.eruptionsChart.init(etna.eruptions, @map, @circleLayer, @markerLayer, @lavaLayer) etna.eruptionsChart.drawBarchart(etna.eruptions) + initLavaLayer: () -> + lavaDrawDiv = d3.select(document.body) + .append('div') + .attr('class', 'lava-vec') + lavaDrawSvg = lavaDrawDiv.append('svg') + lavaDrawGroup = lavaDrawSvg.append('g') + @lavaLayer = etna.lavaLayer(@map, lavaDrawSvg, lavaDrawGroup) + @lavaLayer.parent = lavaDrawDiv.node() + @map.addLayer(@lavaLayer) + + initD3Layer: () -> mapDrawDiv = d3.select(document.body) .append('div') .attr('class', 'd3-vec') mapDrawSvg = mapDrawDiv.append('svg') mapDrawGroup = mapDrawSvg.append('g') - lavaDrawGroup = mapDrawSvg.append('g') - @circleLayer = etna.d3layer(@map, mapDrawSvg, mapDrawGroup, lavaDrawGroup) + @circleLayer = etna.d3layer(@map, mapDrawSvg, mapDrawGroup) @circleLayer.parent = mapDrawDiv.node() @map.addLayer(@circleLayer) initEvents: () -> - # hide legend and tooltips if users clicks somewhere on the map $("#map").click (event) => @hideLegend() diff --git a/css/barchart.css b/css/barchart.css index 0a4aea4..f1a1ccb 100644 --- a/css/barchart.css +++ b/css/barchart.css @@ -3,24 +3,14 @@ z-index: 100; } -/* make sure markers layer is always on top for tooltips */ - -/* -div.markers div.marker-tooltip { - z-index: 1000 !important; -} - This doesn't help since the z-index of the parent element overrules - => the parents z-index is set by mapbox so I changed the mapbox API code -*/ -/* -.markers { - z-index: 99 !important; -} -*/ .d3-vec { z-index: 1; } +.lava-vec { + z-index: 1; +} + .axis path, .axis line { fill: none; @@ -50,11 +40,12 @@ div.markers div.marker-tooltip { circle { /*fill: #993341;*/ fill: #ccc; - fill-opacity: .2; + fill-opacity: .1; stroke: #fff; stroke-width: 1.5px; } -path { +.lava-vec { position: absolute; } +.lava-vec path { opacity: 0.2; } diff --git a/css/story.css b/css/story.css index be449ab..38f3464 100644 --- a/css/story.css +++ b/css/story.css @@ -58,7 +58,7 @@ div.section-title.introduction .intro-title p { } .section-title.cs171 { - background-color: #000; + background-color: #bbb; } .section-title h2 { diff --git a/data/etna-history-with-events.json b/data/etna-history-with-events.json index da53a12..e4044e2 100644 --- a/data/etna-history-with-events.json +++ b/data/etna-history-with-events.json @@ -4,7 +4,7 @@ "craters": [], "sideeffects": [], "airportShutdown": ["catania"], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": [] }, @@ -12,7 +12,7 @@ "vei": 2, "craters": ["SouthEast", "Bocca Nuova", "NorthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": [] }, @@ -21,9 +21,9 @@ "craters": ["Voragine"], "sideeffects": ["earthquakes"], "earthquake": ["Voragine"], - "lavaflows": { - "?": 6.5 - }, + "lavaflows": [ + {"crater": "Voragine", "flow": {"direction": "SSouthEast", "length": 6.5}} + ], "ash": [], "destroyed": [] }, @@ -32,10 +32,10 @@ "craters": ["SouthEast"], "sideeffects": ["airportShutdown"], "airportShutdown": ["catania"], - "lavaflows": { - "Valle del Bove": 4.5, - "Valle del Bove": 6.2 - }, + "lavaflows": [ + {"crater": "SouthEast", "flow": {"direction": "SSouthEast", "length": 4.5}}, + {"crater": "SouthEast", "flow": {"direction": "SSouthEast", "length": 6.2}} + ], "ash": ["EastFlank, NorthFlank"], "destroyed": [] }, @@ -44,7 +44,9 @@ "craters": ["SouthEast"], "sideeffects": [], "airportShutdown": ["catania"], - "lavaflows": {}, + "lavaflows": [ + {"crater": "SouthEast", "flow": {"direction": "ESouthEast", "length": 1.3}} + ], "ash": [], "destroyed": [] }, @@ -52,9 +54,9 @@ "vei": 1, "craters": ["SouthEast"], "sideeffects": [], - "lavaflows": { - "Valle del Bove": 3 - }, + "lavaflows": [ + {"crater": "SouthEast", "flow": {"direction": "SSouthEast", "length": 3}} + ], "ash": [], "destroyed": [] }, @@ -62,7 +64,7 @@ "vei": 1, "craters": ["SouthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": [] }, @@ -70,7 +72,7 @@ "vei": 1, "craters": ["SouthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": [] }, @@ -80,7 +82,9 @@ "sideeffects": ["deformation", "touristStationDestroyed"], "airportShutdown": ["catania"], "earthquake": ["zafferana"], - "lavaflows": {}, + "lavaflows": [ + {"crater": "Voragine", "flow": {"direction": "South", "length": 10.5}} + ], "ash": ["catania", "belpasso"], "destroyed": [] }, @@ -88,9 +92,9 @@ "vei": 1, "craters": ["Voragine"], "sideeffects": [], - "lavaflows": { - "South": 0.5 - }, + "lavaflows": [ + {"crater": "Voragine", "flow": {"direction": "South", "length": 0.5}} + ], "ash": ["giarre"], "destroyed": [] }, @@ -98,7 +102,7 @@ "vei": 1, "craters": ["Voragine"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": ["catania", "siracusa", "zafferana"], "destroyed": [] }, @@ -107,7 +111,7 @@ "craters": ["NorthEast"], "sideeffects": ["airportShutdown"], "airportShutdown": ["catania"], - "lavaflows": {}, + "lavaflows": [], "ash": ["giarre"], "destroyed": [] }, @@ -116,9 +120,9 @@ "craters": ["SouthEast"], "sideeffects": ["airportShutdown"], "airportShutdown": ["catania"], - "lavaflows": { - "SSouthEast": "upToZafferana" - }, + "lavaflows": [ + {"crater": "SouthEast", "flow": {"direction": "ESouthEast", "length": 5}} + ], "ash": [], "destroyed": [] }, @@ -126,7 +130,7 @@ "vei": 2, "craters": ["SouthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": [] }, @@ -134,7 +138,7 @@ "vei": 1, "craters": ["SouthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": ["NorthWestFlank", "Barcellona Pozzo di Grotto"], "destroyed": [] }, @@ -143,9 +147,9 @@ "craters": ["NorthEast"], "sideeffects": ["airportShutdown"], "airportShutdown": ["catania"], - "lavaflows": { - "SouthEast": 1.5 - }, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "SouthEast", "length": 1.5}} + ], "ash": ["SouthEastFlank", "siracusa"], "destroyed": [] }, @@ -153,9 +157,9 @@ "vei": 1, "craters": ["Bocca Nuova"], "sideeffects": [], - "lavaflows": { - "South": 6.5 - }, + "lavaflows": [ + {"crater": "Bocca Nuova", "flow": {"direction": "South", "length": 6.5}} + ], "ash": [], "destroyed": [] }, @@ -163,9 +167,9 @@ "vei": 2, "craters": ["NorthEast"], "sideeffects": [], - "lavaflows": { - "North": 6 - }, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "NNorthWest", "length": 18}} + ], "ash": [], "destroyed": [] }, @@ -174,15 +178,20 @@ "craters": ["SouthEast"], "sideeffects": ["airportShutdown"], "airportShutdown": ["catania"], - "lavaflows": {}, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "NNorthEast", "length": 7}} + ], "ash": ["catania", "siracusa"], "destroyed": [] }, "1971": { "vei": 2, - "craters": [], + "craters": ["NorthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "ESouthEast", "length": 6.5}}, + {"crater": "Bocca Nuova", "flow": {"direction": "South", "length": 2.4}} + ], "ash": [], "destroyed": [] }, @@ -190,15 +199,17 @@ "vei": 1, "craters": ["Voragine"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": ["taormina", "messina"], "destroyed": [] }, "1949": { "vei": 2, - "craters": [], + "craters": ["Voragine"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [ + {"crater": "Voragine", "flow": {"direction": "WNorthWest", "length": 7.2}} + ], "ash": [], "destroyed": [] }, @@ -206,7 +217,9 @@ "vei": 3, "craters": ["NorthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "North", "length": 11.4}} + ], "ash": ["EastFlank", "giarre"], "destroyed": [] }, @@ -214,7 +227,7 @@ "vei": 2, "craters": ["NorthEast"], "sideeffects": [], - "lavaflows": {}, + "lavaflows": [], "ash": ["EastFlank", "messina"], "destroyed": [] }, @@ -222,19 +235,30 @@ "vei": 1, "craters": ["NorthEast"], "sideeffects": [], - "lavaflows": { - "East": 17 - }, + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "East", "length": 17}} + ], "ash": ["EastFlank", "messina"], "destroyed": ["mascali"] }, + "1923": { + "vei": 1, + "craters": ["NorthEast"], + "sideeffects": [], + "lavaflows": [ + {"crater": "NorthEast", "flow": {"direction": "NNorthEast", "length": 15}}, + {"crater": "NorthEast", "flow": {"direction": "North", "length": 10}} + ], + "ash": [], + "destroyed": [] + }, "1910": { "vei": 1, "craters": ["Voragine", "SouthEast", "Bocca Nuova", "NorthEast"], "sideeffects": [], - "lavaflows": { - "South": 15.6 - }, + "lavaflows": [ + {"crater": "Voragine", "flow": {"direction": "South", "length": 15.6}} + ], "ash": [], "destroyed": ["cavaliere"] }, @@ -243,7 +267,7 @@ "craters": ["SouthEast"], "sideeffects": ["earthquakes"], "earthquake": ["messina"], - "lavaflows": {}, + "lavaflows": [], "ash": [], "destroyed": ["messina"] } diff --git a/img/lavaflow-icon.png b/img/lavaflow-icon.png new file mode 100644 index 0000000..048b07a Binary files /dev/null and b/img/lavaflow-icon.png differ diff --git a/index.html b/index.html index 931dce9..ee13c3b 100644 --- a/index.html +++ b/index.html @@ -20,6 +20,7 @@ + @@ -318,6 +319,7 @@