tilegram

package module
v0.0.0-...-e65fb67 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 4, 2017 License: MIT Imports: 9 Imported by: 0

README

tilegram

tilegram is a utility for creating tilegrams

Documentation: GoDoc

Documentation

Overview

Package tilegram contains functions for creating cartograms and hexagonal tilegrams. Tilegrams are cartographic maps that spatially allocate data to regular shapes according to both the geographic location of the data and a weighting function.

Example

This example creates a hexagonal tilegram from census block-group level population data for the US state of Washington. The output from this example can be seen at: https://github.com/ctessum/tilegram/blob/master/testdata/hex_golden.png

// Open the shapefile with the census data.
d, err := shp.NewDecoder("testdata/WA_Population_2010.shp")
if err != nil {
	log.Panic(err)
}
defer d.Close()

var data censusDensity
var blckgrps []geom.Polygon // This will hold the census block group geometry.
var population []float64
// Read in the census data.
for {
	var rec censusData
	if more := d.DecodeRow(&rec); !more {
		break
	}
	data = append(data, rec)
	population = append(population, rec.Population)
	blckgrps = append(blckgrps, rec.Polygon)
}
if err = d.Error(); err != nil {
	log.Panic(err)
}

const (
	margin = 500000.0 // meters
	rows   = 512      //256
	cols   = 1024     //512
)
cartogram := NewCartogram(data, margin, rows, cols)
cartogram.Blur = 3
blckgrps2 := cartogram.TransformPolygons(blckgrps)

h := plotter.NewHeatMap(cartogram, palette.Heat(12, 1))
plt, err := plot.New()
if err != nil {
	log.Panic(err)
}
plt.Add(h)
if err = plt.Save(3*vg.Inch, 2*vg.Inch, "density.png"); err != nil {
	log.Panic(err)
}

var records []Grouper
for i, b := range blckgrps2 {
	records = append(records, &Data{
		Polygonal: b,
		W:         data[i].Population,
		G:         data[i].County,
	})
}

const r = 20000.0 // This is the hexagon radius in meters.
hex, err := NewHexagram(records, r)
if err != nil {
	log.Panic(err)
}

// Make plots of the results.
// Everything below here is unrelated to the creation
// of the tilegrams themselves.

const (
	// These are the figure dimensions.
	width        = 20 * vg.Centimeter
	height       = 6 * vg.Centimeter
	legendHeight = 0.9 * vg.Centimeter
)

// Create a canvas to put our plot on.
img := vgimg.NewWith(vgimg.UseWH(width, height), vgimg.UseDPI(300))
dc := draw.New(img)
legendc := draw.Crop(dc, 0, 0, 0, legendHeight-dc.Max.Y+dc.Min.Y)
dc = draw.Crop(dc, 0, 0, legendHeight, 0)
tiles := draw.Tiles{
	Cols: 3,
	Rows: 1,
}
lineStyle := draw.LineStyle{Width: 0.1 * vg.Millimeter}

// First, make a map of our unaltered census data.
cmap := carto.NewColorMap(carto.Linear)
cmap.AddArray(population)
panelA := tiles.At(dc, 0, 0)
panelB := tiles.At(dc, 1, 0)
cmap.Set()
lc := tiles.At(legendc, 0, 0)
cmap.Legend(&lc, "Population")
b := hex.Bounds()
m := carto.NewCanvas(b.Max.Y, b.Min.Y, b.Max.X, b.Min.X, panelA)
for i, rec := range records {
	color := cmap.GetColor(rec.Weight())
	lineStyle.Color = color
	m.DrawVector(blckgrps[i], color, lineStyle, draw.GlyphStyle{})
}

cartoPopDensity := make([]float64, len(blckgrps2))
for i, g := range blckgrps2 {
	cartoPopDensity[i] = population[i] / g.Area() * 1e6
}
avg := stat.Mean(cartoPopDensity, nil)
floats.AddConst(-avg, cartoPopDensity)
floats.Scale(1/avg, cartoPopDensity)
cmap2 := carto.NewColorMap(carto.LinCutoff)
cmap2.AddArray(cartoPopDensity)
cmap2.NumDivisions = 7
cmap2.Set()
lc2 := tiles.At(legendc, 1, 0)
cmap2.Legend(&lc2, "(Population/km² - mean) / mean")

m2 := carto.NewCanvas(b.Max.Y, b.Min.Y, b.Max.X, b.Min.X, panelB)
for i, blckgrp := range blckgrps2 {
	color := cmap2.GetColor(cartoPopDensity[i])
	lineStyle.Color = color
	m2.DrawVector(blckgrp, color, lineStyle, draw.GlyphStyle{})
}

// Now, draw the county boundaries.
d2, err := shp.NewDecoder("testdata/WA_Counties_2010.shp")
if err != nil {
	log.Panic(err)
}
defer d2.Close()

type countyData struct {
	geom.Polygon
}
var counties []geom.Polygon
for {
	var rec countyData
	if more := d2.DecodeRow(&rec); !more {
		break
	}
	counties = append(counties, rec.Polygon)
	m.DrawVector(rec.Polygon, color.NRGBA{}, draw.LineStyle{
		Width: 0.25 * vg.Millimeter,
		Color: color.Black,
	}, draw.GlyphStyle{})
}
if err = d2.Error(); err != nil {
	log.Panic(err)
}

counties2 := cartogram.TransformPolygons(counties)
for _, c := range counties2 {
	m2.DrawVector(c, color.NRGBA{}, draw.LineStyle{
		Width: 0.25 * vg.Millimeter,
		Color: color.Black,
	}, draw.GlyphStyle{})
}

// Next, make a maps of our tilegrams.
weights := make([]float64, hex.Len())
for i := 0; i < hex.Len(); i++ {
	weights[i] = hex.Weight(i)
}
avg = stat.Mean(weights, nil)
floats.AddConst(-avg, weights)
floats.Scale(1/avg, weights)

cmap = carto.NewColorMap(carto.Linear)
cmap.AddArray(weights)
panelC := tiles.At(dc, 2, 0)
cmap.Set()
lc = tiles.At(legendc, 2, 0)
cmap.Legend(&lc, "(Population-mean)/mean")
m = carto.NewCanvas(b.Max.Y, b.Min.Y, b.Max.X, b.Min.X, panelC)
for i, hex := range hex.Hexes() {
	c := cmap.GetColor(weights[i])
	lineStyle.Color = c
	m.DrawVector(hex.Geom(), c, lineStyle, draw.GlyphStyle{})
}
// Now we add our county boundaries.
for _, g := range hex.GroupGeom(r / 2) {
	m.DrawVector(g, color.NRGBA{}, draw.LineStyle{
		Width: 0.25 * vg.Millimeter,
		Color: color.Black,
	}, draw.GlyphStyle{})
}

// Save the file
f, err := os.Create("testdata/hex.png")
if err != nil {
	panic(err)
}
pngc := vgimg.PngCanvas{Canvas: img}
if _, err := pngc.WriteTo(f); err != nil {
	panic(err)
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Cartogram

type Cartogram struct {

	// Blur is the radius (in pixels) for Gaussian blurring.
	Blur float64
	// contains filtered or unexported fields
}

Cartogram holds information for cartogram creation.

func NewCartogram

func NewCartogram(shapes PolygonDensity, margin float64, rows, cols int) *Cartogram

NewCartogram creates a cartogram with the given margin added to each border of the matrix and the given numbers of rows and columns.

It applies the cartogram creation algorithm described in the article below:

Gastner, M. T., & Newman, M. E. J. (2004). Diffusion-based method for producing density-equalizing maps. Proc. Nat. Acad. of Sci., 101(20), 7499–7504. http://doi.org/10.1073/pnas.0400280101

func (*Cartogram) Destroy

func (c *Cartogram) Destroy()

Destroy frees the memory associated with the receiver. No other cartogram can be created before this happens.

func (*Cartogram) Dims

func (c *Cartogram) Dims() (cols, rows int)

func (*Cartogram) TransformPath

func (c *Cartogram) TransformPath(p geom.Path) geom.Path

func (*Cartogram) TransformPoint

func (c *Cartogram) TransformPoint(p geom.Point) geom.Point

TransformPoint moves a point to match a cartogram.

func (*Cartogram) TransformPolygons

func (c *Cartogram) TransformPolygons(p []geom.Polygon) []geom.Polygon

func (*Cartogram) X

func (c *Cartogram) X(col int) float64

func (*Cartogram) Y

func (c *Cartogram) Y(row int) float64

func (*Cartogram) Z

func (c *Cartogram) Z(col, row int) float64

type Data

type Data struct {
	geom.Polygonal
	W float64 // Weight
	G string  // Group
}

Data is an implementation of the Grouper interface.

func (*Data) Group

func (d *Data) Group() string

Group implements the Grouper interface.

func (*Data) Weight

func (d *Data) Weight() float64

Weight implements the Grouper interface.

type Grouper

type Grouper interface {
	geom.Polygonal

	// Weight is the value of the characteristic attribute
	// of the receiver. Population count is a typical weight value.
	Weight() float64

	// Group returns the group that the receiver belongs to.
	// All values in a given tile are required to be of the same group.
	// Typical groups would be political units such as states or provinces.
	Group() string
}

Grouper is a holder for an individual spatial unit that is to be assigned to one of the tile groups in a tilegram.

type Hex

type Hex struct {
	// Point is the geometric center of this hexagon.
	geom.Point

	// Data holds the data points that are assigned to this Hex.
	Data []Grouper
	// contains filtered or unexported fields
}

Hex represents an individual hexagonol tile in a tilegram.

func (*Hex) Bounds

func (h *Hex) Bounds() *geom.Bounds

Bounds returns the bounds of the hexagon.

func (*Hex) Geom

func (h *Hex) Geom() geom.Polygon

Geom returns the geometry of the receiver when given the radius of the hexagon.

func (*Hex) Group

func (h *Hex) Group() string

Group returns the group which has the most weight among the data items in the receiver. If the receiver does not have any data items, the group will be "".

func (*Hex) Weight

func (h *Hex) Weight() float64

Weight returns the sum of the weights of the data items in the receiver.

type Hexagram

type Hexagram struct {
	// contains filtered or unexported fields
}

Hexagram is a hexagonal tilegram.

func NewHexagram

func NewHexagram(data []Grouper, r float64) (*Hexagram, error)

NewHexagram creates a new hexagonal tile map, where bounds is the geometric boundary for the tiles, and r is the radius of each hexagonal tile.

func (*Hexagram) Bounds

func (h *Hexagram) Bounds() *geom.Bounds

Bounds returns the bounding box of the receiver.

func (*Hexagram) GroupGeom

func (h *Hexagram) GroupGeom(tolerance float64) map[string]geom.Polygon

GroupGeom returns the combined geomtry of the hexagons in each group, where tolerance is the distance two points can be apart while still being considered as in the same location. tolerance can be used to avoid polygon slivers in the result.

func (*Hexagram) Hexes

func (h *Hexagram) Hexes() []*Hex

Hexes returns the Hex tiles that comprise the receiver.

func (*Hexagram) Len

func (h *Hexagram) Len() int

Len returns the number of tiles in the receiver.

func (*Hexagram) Weight

func (h *Hexagram) Weight(i int) float64

Weight returns the sum of the weights of the data items in tile i.

type PolygonDensity

type PolygonDensity interface {
	Len() int
	Polygon(int) geom.Polygonal
	Density(int) float64
}

PolygonDensity defines the inputs for cartogram creation.

Directories

Path Synopsis
internal

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL