-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
186 additions
and
152 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package density | ||
|
||
import "math" | ||
|
||
type KernelItem struct { | ||
Dx, Dy int | ||
Weight float64 | ||
} | ||
|
||
type Kernel []KernelItem | ||
|
||
func NewKernel(n int) Kernel { | ||
var result Kernel | ||
for dy := -n; dy <= n; dy++ { | ||
for dx := -n; dx <= n; dx++ { | ||
d := math.Sqrt(float64(dx*dx + dy*dy)) | ||
w := math.Max(0, 1-d/float64(n)) | ||
w = math.Pow(w, 2) | ||
result = append(result, KernelItem{dx, dy, w}) | ||
} | ||
} | ||
return result | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package density | ||
|
||
import ( | ||
"fmt" | ||
"image" | ||
"log" | ||
"math" | ||
|
||
"github.com/gocql/gocql" | ||
) | ||
|
||
type Renderer struct { | ||
Cluster *gocql.ClusterConfig | ||
Query string | ||
BaseZoom int | ||
} | ||
|
||
func NewRenderer(host, keyspace, table string, baseZoom int) *Renderer { | ||
cluster := gocql.NewCluster(host) | ||
cluster.Keyspace = keyspace | ||
query := "SELECT lat, lng FROM %s WHERE zoom = ? AND x = ? AND y = ?;" | ||
query = fmt.Sprintf(query, table) | ||
return &Renderer{cluster, query, baseZoom} | ||
} | ||
|
||
func (r *Renderer) Render(zoom, x, y int) (image.Image, bool) { | ||
session, _ := r.Cluster.CreateSession() | ||
defer session.Close() | ||
tile := r.loadTile(session, zoom, x, y) | ||
kernel := NewKernel(2) | ||
scale := 32 / math.Pow(4, float64(r.BaseZoom-zoom)) | ||
return tile.Render(kernel, scale) | ||
} | ||
|
||
func (r *Renderer) loadPoints(session *gocql.Session, x, y int, tile *Tile) { | ||
iter := session.Query(r.Query, r.BaseZoom, x, y).Iter() | ||
var lat, lng float64 | ||
for iter.Scan(&lat, &lng) { | ||
tile.Add(lat, lng) | ||
} | ||
if err := iter.Close(); err != nil { | ||
log.Fatal(err) | ||
} | ||
} | ||
|
||
func (r *Renderer) loadTile(session *gocql.Session, zoom, x, y int) *Tile { | ||
tile := NewTile(zoom, x, y) | ||
if zoom < 12 { | ||
return tile | ||
} | ||
p := 1 // padding | ||
var x0, y0, x1, y1 int | ||
if zoom > r.BaseZoom { | ||
d := int(math.Pow(2, float64(zoom-r.BaseZoom))) | ||
x0, y0 = x/d-p, y/d-p | ||
x1, y1 = x/d+p, y/d+p | ||
} else if zoom < r.BaseZoom { | ||
d := int(math.Pow(2, float64(r.BaseZoom-zoom))) | ||
x0, y0 = x*d-p, y*d-p | ||
x1, y1 = (x+1)*d-1+p, (y+1)*d-1+p | ||
} else { | ||
x0, y0 = x-p, y-p | ||
x1, y1 = x+p, y+p | ||
} | ||
for tx := x0; tx <= x1; tx++ { | ||
for ty := y0; ty <= y1; ty++ { | ||
r.loadPoints(session, tx, ty, tile) | ||
} | ||
} | ||
return tile | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package density | ||
|
||
import ( | ||
"image" | ||
"image/color" | ||
"math" | ||
|
||
"github.com/lucasb-eyer/go-colorful" | ||
) | ||
|
||
const TileSize = 256 | ||
|
||
func TileXY(zoom int, lat, lng float64) (x int, y int) { | ||
x = int(math.Floor((lng + 180.0) / 360.0 * (math.Exp2(float64(zoom))))) | ||
y = int(math.Floor((1.0 - math.Log(math.Tan(lat*math.Pi/180.0)+1.0/math.Cos(lat*math.Pi/180.0))/math.Pi) / 2.0 * (math.Exp2(float64(zoom))))) | ||
return | ||
} | ||
|
||
func TileLatLng(zoom, x, y int) (lat, lng float64) { | ||
n := math.Pi - 2.0*math.Pi*float64(y)/math.Exp2(float64(zoom)) | ||
lat = 180.0 / math.Pi * math.Atan(0.5*(math.Exp(n)-math.Exp(-n))) | ||
lng = float64(x)/math.Exp2(float64(zoom))*360.0 - 180.0 | ||
return | ||
} | ||
|
||
type IntPoint struct { | ||
X, Y int | ||
} | ||
|
||
type Tile struct { | ||
Zoom, X, Y int | ||
Lat0, Lng0 float64 | ||
Lat1, Lng1 float64 | ||
Grid map[IntPoint]float64 | ||
} | ||
|
||
func NewTile(zoom, x, y int) *Tile { | ||
lat1, lng0 := TileLatLng(zoom, x, y) | ||
lat0, lng1 := TileLatLng(zoom, x+1, y+1) | ||
grid := make(map[IntPoint]float64) | ||
return &Tile{zoom, x, y, lat0, lng0, lat1, lng1, grid} | ||
} | ||
|
||
func (tile *Tile) Add(lat, lng float64) { | ||
// TODO: bilinear interpolation | ||
x := int(math.Floor((lng - tile.Lng0) / (tile.Lng1 - tile.Lng0) * TileSize)) | ||
y := int(math.Floor((lat - tile.Lat0) / (tile.Lat1 - tile.Lat0) * TileSize)) | ||
tile.Grid[IntPoint{x, y}]++ | ||
} | ||
|
||
func (tile *Tile) Render(kernel Kernel, scale float64) (image.Image, bool) { | ||
im := image.NewNRGBA(image.Rect(0, 0, TileSize, TileSize)) | ||
ok := false | ||
for y := 0; y < TileSize; y++ { | ||
for x := 0; x < TileSize; x++ { | ||
var t, tw float64 | ||
for _, k := range kernel { | ||
nx := x + k.Dx | ||
ny := y + k.Dy | ||
t += tile.Grid[IntPoint{nx, ny}] * k.Weight | ||
tw += k.Weight | ||
} | ||
if t == 0 { | ||
continue | ||
} | ||
t *= scale | ||
t /= tw | ||
t = t / (t + 1) | ||
a := uint8(255 * math.Pow(t, 0.5)) | ||
c := colorful.Hsv(215.0, 1-t*t, 1) | ||
r, g, b := c.RGB255() | ||
im.SetNRGBA(x, TileSize-1-y, color.NRGBA{r, g, b, a}) | ||
ok = true | ||
} | ||
} | ||
return im, ok | ||
} |
Oops, something went wrong.