text: // URL: https://beta.observablehq.com/@randomfractals/chicago-homicides-sunburst
  // Title: Chicago Homicides Sunburst, 2001-2018
  // Author: Taras Novak (@randomfractals)
  // Version: 444
  // Runtime version: 1
  
  const m0 = {
    id: "d0e1ec4b85f01a90@444",
    variables: [
      {
        inputs: ["md"],
        value: (function(md){return(
  md`# Chicago Homicides Sunburst, 2001-2018
  
  This sundial displays Chicago homicides by 'side' and community area for the time period from January 2001 to August 10 2018.
  
  Chicago is divided into 9 sides and 77 community areas as defined by the Social Science Research Committee at the University of Chicago back in 2012:
  
  https://en.wikipedia.org/wiki/Community_areas_in_Chicago
  
  Data Source: [Chicago Data Portal/Public Safety/Crimes 2001 to present](https://data.cityofchicago.org/Public-Safety/Crimes-2001-to-present/ijzp-q8t2)
  
  *UX tip: click on the inner circle radial feather to zoom in and middle white cirlce to zoom out,
  mouse over for the reported crime counts*
  `
  )})
      },
      {
        name: "chart",
        inputs: ["partition","data","d3","DOM","width","color","arc","format","radius"],
        value: (function(partition,data,d3,DOM,width,color,arc,format,radius)
  {
    const root = partition(data);
  
    root.each(d => d.current = d);
  
    const svg = d3.select(DOM.svg(width, width))
        .style("width", "100%")
        .style("height", "auto")
        .style("font", "10px sans-serif");
  
    const g = svg.append("g")
        .attr("transform", `translate(${width / 2},${width / 2})`);
  
    const path = g.append("g")
      .selectAll("path")
      .data(root.descendants().slice(1))
      .enter().append("path")
        .attr("fill", d => { while (d.depth > 1) d = d.parent; return color(d.data.name); })
        .attr("fill-opacity", d => arcVisible(d.current) ? (d.children ? 0.6 : 0.4) : 0)
        .attr("d", d => arc(d.current));
  
    path.filter(d => d.children)
        .style("cursor", "pointer")
        .on("click", clicked);
  
    path.append("title")
        .text(d => `${format(d.value)} on ${d.ancestors().map(d => d.data.name).reverse().join("/").replace('flare/', '')}`);
  
    const label = g.append("g")
        .attr("pointer-events", "none")
        .attr("text-anchor", "middle")
        .style("user-select", "none")
      .selectAll("text")
      .data(root.descendants().slice(1))
      .enter().append("text")
        .attr("dy", "0.35em")
        .attr("fill-opacity", d => +labelVisible(d.current))
        .attr("transform", d => labelTransform(d.current))
        .text(d => d.data.name);
  
    const parent = g.append("circle")
        .datum(root)
        .attr("r", radius)
        .attr("fill", "none")
        .attr("pointer-events", "all")
        .on("click", clicked);
  
    function clicked(p) {
      parent.datum(p.parent || root);
  
      root.each(d => d.target = {
        x0: Math.max(0, Math.min(1, (d.x0 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
        x1: Math.max(0, Math.min(1, (d.x1 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
        y0: Math.max(0, d.y0 - p.depth),
        y1: Math.max(0, d.y1 - p.depth)
      });
  
      const t = g.transition().duration(750);
  
      // Transition the data on all arcs, even the ones that aren’t visible,
      // so that if this transition is interrupted, entering arcs will start
      // the next transition from the desired position.
      path.transition(t)
          .tween("data", d => {
            const i = d3.interpolate(d.current, d.target);
            return t => d.current = i(t);
          })
        .filter(function(d) {
          return +this.getAttribute("fill-opacity") || arcVisible(d.target);
        })
          .attr("fill-opacity", d => arcVisible(d.target) ? (d.children ? 0.6 : 0.4) : 0)
          .attrTween("d", d => () => arc(d.current));
  
      label.filter(function(d) {
          return +this.getAttribute("fill-opacity") || labelVisible(d.target);
        }).transition(t)
          .attr("fill-opacity", d => +labelVisible(d.target))
          .attrTween("transform", d => () => labelTransform(d.current));
    }
    
    function arcVisible(d) {
      return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0;
    }
  
    function labelVisible(d) {
      return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
    }
  
    function labelTransform(d) {
      const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
      const y = (d.y0 + d.y1) / 2 * radius;
      return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
    }
  
    return svg.node();
  }
  )
      },
      {
        name: "data",
        inputs: ["groupBySide","sides","homicides"],
        value: (function(groupBySide,sides,homicides){return(
  groupBySide(sides, homicides)
  )})
      },
      {
        name: "partition",
        inputs: ["d3"],
        value: (function(d3){return(
  data => {
    const root = d3.hierarchy(data)
        .sum(d => d.size)
        .sort((a, b) => b.value - a.value);
    return d3.partition()
        .size([2 * Math.PI, root.height + 1])
      (root);
  }
  )})
      },
      {
        name: "color",
        inputs: ["d3","data"],
        value: (function(d3,data){return(
  d3.scaleOrdinal().range(d3.quantize(d3.interpolateRainbow, data.children.length + 1))
  )})
      },
      {
        name: "format",
        inputs: ["d3"],
        value: (function(d3){return(
  d3.format(",d")
  )})
      },
      {
        name: "radius",
        inputs: ["width"],
        value: (function(width){return(
  width / 6
  )})
      },
      {
        name: "arc",
        inputs: ["d3","radius"],
        value: (function(d3,radius){return(
  d3.arc()
      .startAngle(d => d.x0)
      .endAngle(d => d.x1)
      .padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
      .padRadius(radius * 1.5)
      .innerRadius(d => d.y0 * radius)
      .outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1))
  )})
      },
      {
        name: "d3",
        inputs: ["require"],
        value: (function(require){return(
  require("https://d3js.org/d3.v5.min.js")
  )})
      },
      {
        from: "@randomfractals/chicago-homicides-treemap",
        name: "sides",
        remote: "sides"
      },
      {
        from: "@randomfractals/chicago-homicides-treemap",
        name: "communities",
        remote: "communities"
      },
      {
        from: "@randomfractals/chicago-homicides-treemap",
        name: "homicides",
        remote: "homicides"
      },
      {
        from: "@randomfractals/chicago-homicides-treemap",
        name: "groupBySide",
        remote: "groupBySide"
      }
    ]
  };
  
  const m1 = {
    id: "@randomfractals/chicago-homicides-treemap",
    variables: [
      {
        name: "sides",
        inputs: ["communityData"],
        value: (function(communityData)
  {
    // group communities by side
    const sides = {}
    communityData.slice(1) // skip 1st null row
      .map(community => {
        if (!sides[community.Side]) {
          sides[community.Side] = [];
        }
        sides[community.Side].push(community);
      });
    return sides;
  }
  )
      },
      {
        name: "communities",
        inputs: ["communityData"],
        value: (function(communityData)
  {
    // create communities info map
    const communities = {}
    communityData.slice(1) // skip 1st null row
      .map(community => communities[community.CommunityName.toLowerCase()] = community);
    return communities;
  }
  )
      },
      {
        name: "homicides",
        inputs: ["d3","dataUrl"],
        value: (function(d3,dataUrl){return(
  d3.csv(dataUrl)
  )})
      },
      {
        name: "groupBySide",
        inputs: ["groupByCommunity"],
        value: (function(groupByCommunity){return(
  function groupBySide(sides, homicides) {
    const communityData = groupByCommunity(homicides);
    return {
      name: 'flare',
      children: Object.keys(sides).map(sideName => {
        return {
          name: sideName, 
          children: sides[sideName].map(community => {
            return {
              name: community.CommunityName, 
              size: communityData[community.CommunityArea].length
            };
          })
        };
      })
    };
  }
  )})
      },
      {
        name: "communityData",
        inputs: ["d3","communitiesInfoUrl"],
        value: (function(d3,communitiesInfoUrl){return(
  d3.csv(communitiesInfoUrl)
  )})
      },
      {
        name: "d3",
        inputs: ["require"],
        value: (function(require){return(
  require("https://d3js.org/d3.v5.min.js")
  )})
      },
      {
        name: "dataUrl",
        value: (function(){return(
  'https://raw.githubusercontent.com/RandomFractals/ChicagoCrimes/master/data/2018/chicago-homicides-2001-2018.csv'
  )})
      },
      {
        name: "groupByCommunity",
        value: (function(){return(
  function groupByCommunity (data) {
    const communityData = {};
    data.map(d => {
      const communityId = d['Community Area'];
      if (!communityData[communityId]) {
        communityData[communityId] = [];
      }
      communityData[communityId].push(d);
    });
    return communityData;  
  }
  )})
      },
      {
        name: "communitiesInfoUrl",
        value: (function(){return(
  'https://raw.githubusercontent.com/RandomFractals/ChicagoCrimes/master/data/chicago-community-areas.csv'
  )})
      }
    ]
  };
  
  const notebook = {
    id: "d0e1ec4b85f01a90@444",
    modules: [m0,m1]
  };
  
  export default notebook;
  