Skip to content

Instantly share code, notes, and snippets.

@jsanz
Last active September 14, 2016 09:01
Show Gist options
  • Save jsanz/59295668f7af7399ddc8e911ab329ff3 to your computer and use it in GitHub Desktop.
Save jsanz/59295668f7af7399ddc8e911ab329ff3 to your computer and use it in GitHub Desktop.
Talk: Overviews Overview
@import url(https://fonts.googleapis.com/css?family=Montserrat:700);
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,700,400italic,700italic);
/**
* Black theme for reveal.js.
*
* Copyright (C) 2011-2012 Hakim El Hattab, http://hakim.se
*/
/*********************************************
* GLOBAL STYLES
*********************************************/
body {
background: #111;
background-color: #111; }
.reveal {
font-family: 'Open Sans', sans-serif;
font-size: 30px;
font-weight: normal;
color: #eee; }
::selection {
color: #fff;
background: #e7ad52;
text-shadow: none; }
.reveal .slides > section, .reveal .slides > section > section {
line-height: 1.3;
font-weight: inherit; }
/*********************************************
* HEADERS
*********************************************/
.reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 {
margin: 0 0 20px 0;
color: #eee;
font-family: 'Montserrat', Impact, sans-serif;
font-weight: normal;
line-height: 1.2;
letter-spacing: -0.03em;
text-transform: none;
text-shadow: none;
word-wrap: break-word; }
.reveal h1 {
font-size: 3.77em; }
.reveal h2 {
font-size: 2.11em; }
.reveal h3 {
font-size: 1.55em; }
.reveal h4 {
font-size: 1em; }
.reveal h1 {
text-shadow: none; }
/*********************************************
* OTHER
*********************************************/
.reveal p {
margin: 20px 0;
line-height: 1.3; }
/* Ensure certain elements are never larger than the slide itself */
.reveal img, .reveal video, .reveal iframe {
max-width: 95%;
max-height: 95%; }
.reveal strong, .reveal b {
font-weight: bold; }
.reveal em {
font-style: italic; }
.reveal ol, .reveal dl, .reveal ul {
display: inline-block;
text-align: left;
margin: 0 0 0 1em; }
.reveal ol {
list-style-type: decimal; }
.reveal ul {
list-style-type: disc; }
.reveal ul ul {
list-style-type: square; }
.reveal ul ul ul {
list-style-type: circle; }
.reveal ul ul, .reveal ul ol, .reveal ol ol, .reveal ol ul {
display: block;
margin-left: 40px; }
.reveal dt {
font-weight: bold; }
.reveal dd {
margin-left: 40px; }
.reveal q, .reveal blockquote {
quotes: none; }
.reveal blockquote {
display: block;
position: relative;
width: 70%;
margin: 20px auto;
padding: 5px;
font-style: italic;
background: rgba(255, 255, 255, 0.05);
box-shadow: 0px 0px 2px rgba(0, 0, 0, 0.2); }
.reveal blockquote p:first-child, .reveal blockquote p:last-child {
display: inline-block; }
.reveal q {
font-style: italic; }
.reveal pre {
display: block;
position: relative;
width: 90%;
margin: 20px auto;
text-align: left;
font-size: 0.55em;
font-family: monospace;
line-height: 1.2em;
word-wrap: break-word;
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.3); }
.reveal code {
font-family: monospace; }
.reveal pre code {
display: block;
padding: 5px;
overflow: auto;
max-height: 400px;
word-wrap: normal;
background: #3F3F3F;
color: #DCDCDC; }
.reveal table {
margin: auto;
border-collapse: collapse;
border-spacing: 0; }
.reveal table th {
font-weight: bold; }
.reveal table th, .reveal table td {
text-align: left;
padding: 0.2em 0.5em 0.2em 0.5em;
border-bottom: 1px solid; }
.reveal table th[align="center"], .reveal table td[align="center"] {
text-align: center; }
.reveal table th[align="right"], .reveal table td[align="right"] {
text-align: right; }
.reveal table tr:last-child td {
border-bottom: none; }
.reveal sup {
vertical-align: super; }
.reveal sub {
vertical-align: sub; }
.reveal small {
display: inline-block;
font-size: 0.6em;
line-height: 1.2em;
vertical-align: top; }
.reveal small * {
vertical-align: top; }
/*********************************************
* LINKS
*********************************************/
.reveal a {
color: #e7ad52;
text-decoration: none;
-webkit-transition: color 0.15s ease;
-moz-transition: color 0.15s ease;
transition: color 0.15s ease; }
.reveal a:hover {
color: #f3d7ac;
text-shadow: none;
border: none; }
.reveal .roll span:after {
color: #fff;
background: #d0881d; }
/*********************************************
* IMAGES
*********************************************/
.reveal section img {
margin: 15px 0px;
background: rgba(255, 255, 255, 0.12);
border: 4px solid #eee;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.15); }
.reveal a img {
-webkit-transition: all 0.15s linear;
-moz-transition: all 0.15s linear;
transition: all 0.15s linear; }
.reveal a:hover img {
background: rgba(255, 255, 255, 0.2);
border-color: #e7ad52;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.55); }
/*********************************************
* NAVIGATION CONTROLS
*********************************************/
.reveal .controls div.navigate-left, .reveal .controls div.navigate-left.enabled {
border-right-color: #e7ad52; }
.reveal .controls div.navigate-right, .reveal .controls div.navigate-right.enabled {
border-left-color: #e7ad52; }
.reveal .controls div.navigate-up, .reveal .controls div.navigate-up.enabled {
border-bottom-color: #e7ad52; }
.reveal .controls div.navigate-down, .reveal .controls div.navigate-down.enabled {
border-top-color: #e7ad52; }
.reveal .controls div.navigate-left.enabled:hover {
border-right-color: #f3d7ac; }
.reveal .controls div.navigate-right.enabled:hover {
border-left-color: #f3d7ac; }
.reveal .controls div.navigate-up.enabled:hover {
border-bottom-color: #f3d7ac; }
.reveal .controls div.navigate-down.enabled:hover {
border-top-color: #f3d7ac; }
/*********************************************
* PROGRESS BAR
*********************************************/
.reveal .progress {
background: rgba(0, 0, 0, 0.2); }
.reveal .progress span {
background: #e7ad52;
-webkit-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
-moz-transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985);
transition: width 800ms cubic-bezier(0.26, 0.86, 0.44, 0.985); }
/*********************************************
* SLIDE NUMBER
*********************************************/
.reveal .slide-number {
color: #e7ad52; }
/*************************
CARTODB OVERRIDES
**************************/
body {
background: #2d3b42;
background-color: #2d3b42;
}
.reveal section img {
border:none;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<link rel="stylesheet" href="http://lab.hakim.se/reveal-js/css/reveal.css">
<link rel="stylesheet" href="cartodb.css" id="theme">
<link rel="stylesheet" href="http://lab.hakim.se/reveal-js/lib/css/zenburn.css">
<!-- If the query includes 'print-pdf', use the PDF print sheet -->
<script>
document.write( '<link rel="stylesheet" href="http://lab.hakim.se/reveal-js/css/print/' + ( window.location.search.match( /print-pdf/gi ) ? 'pdf' : 'paper' ) + '.css" type="text/css" media="print">' );
</script>
<!--[if lt IE 9]>
<script src="http://lab.hakim.se/reveal-js/lib/js/html5shiv.js"></script>
<![endif]-->
</head>
<body>
<div class="reveal">
<div class="slides">
<section data-markdown="overviews.md"
data-separator="^---"
data-separator-vertical="^___"
data-charset="utf-8"
data-notes="^Note:">
</section>
</div>
</div>
<script src="http://lab.hakim.se/reveal-js/lib/js/head.min.js"></script>
<script src="http://lab.hakim.se/reveal-js/js/reveal.js"></script>
<script>
Reveal.initialize({
width: 960,
height: 700,
controls: true,
progress: true,
history: true,
center: true,
theme: Reveal.getQueryHash().theme, // available themes are in /css/theme
transition: Reveal.getQueryHash().transition || 'default', // default/cube/page/concave/zoom/linear/fade/none
// Optional libraries used to extend on reveal.js
dependencies: [
{ src: 'http://lab.hakim.se/reveal-js/lib/js/classList.js', condition: function() { return !document.body.classList; } },
{ src: 'http://lab.hakim.se/reveal-js/plugin/markdown/marked.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'http://lab.hakim.se/reveal-js/plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
{ src: 'http://lab.hakim.se/reveal-js/plugin/highlight/highlight.js', async: true, callback: function() { hljs.initHighlightingOnLoad(); } },
{ src: 'http://lab.hakim.se/reveal-js/plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } },
{ src: 'http://lab.hakim.se/reveal-js/plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }
]
});
</script>
</body>
</html>
Display the source blob
Display the rendered blob
Raw
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

Point Overviews2

  • Solutions Summit 2016
  • Jorge Sanz

The problem

  • How to render points between 1M and 50M
  • Point rendering time is linear
  • We need to reduce the number of points
    sent to the tiler (aggregate)

Overviews, the old way


Helper function

Used to convert scale denominator to zoom level

CREATE OR REPLACE FUNCTION zoom(scaledenominator numeric)
RETURNS integer
AS $$
BEGIN
	CASE
		WHEN scaleDenominator > 1000000000
			THEN RETURN 0;
		WHEN scaleDenominator <= 1000000000 AND scaleDenominator > 500000000
			THEN RETURN 1;
		WHEN scaleDenominator <= 500000000 AND scaleDenominator > 200000000
			THEN RETURN 2;
		WHEN scaleDenominator <= 200000000 AND scaleDenominator > 100000000
			THEN RETURN 3;
		...
		WHEN scaleDenominator <= 100
		THEN RETURN 23;
	END CASE;
END;
$$ language 'plpgsql';

Materialized views

One per zoom level

CREATE MATERIALIZED view ov_0_pois AS
WITH par AS (
	WITH innerpar AS (
			SELECT 1.0/(CDB_XYZ_Resolution(0)*2) AS resinv  -- Zoom level here
	)
	SELECT
		CDB_XYZ_Resolution(0)*2 AS res, -- Zoom level here
		innerpar.resinv AS resinv
	FROM innerpar
), geo AS (
	SELECT
		floor(st_x(i.the_geom_webmercator)*resinv) AS xx,
		floor(st_y(i.the_geom_webmercator)*resinv) AS yy,
		count(cartodb_id) AS c -- Aggregation function here
	FROM (
		SELECT * FROM pois -- Original data here
	) i, par p
	GROUP BY xx, yy
)
SELECT
	c,
	st_setsrid(st_makepoint(
			xx*CDB_XYZ_Resolution(0)*2, -- Zoom level here
			yy*CDB_XYZ_Resolution(0)*2  -- Zoom level here
		),3857) AS the_geom_webmercator
FROM geo;

CREATE INDEX ov_0_pois_geom_idx ON ov_0_pois USING GIST(the_geom_webmercator);
GRANT SELECT ON ov_0_pois TO tileuser;
GRANT SELECT ON ov_0_pois TO publicuser;

Management

  • Two sets of materialized views
  • Refresh the one not used
  • Swap all of them in one transaction
  • Sleep until tomorrow

Rendering

Using the !scale_denominator! mapnik variable to render different geometries based on the zoom level.

WITH a AS (
		 SELECT zoom('!scale_denominator!') AS current_zoom -- zoom_level
)
SELECT
	the_geom_webmercator,c,null AS cat
FROM ov_0_pois, a WHERE current_zoom = 0 UNION -- union zoom level 0
SELECT
	the_geom_webmercator,c,null AS cat
FROM ov_1_pois, a WHERE current_zoom = 1 UNION -- union zoom level 1
...
SELECT
	the_geom_webmercator,c,null AS cat
FROM ov_13_pois, a WHERE current_zoom = 13 UNION -- union zoom level 13
SELECT
	the_geom_webmercator,
	1 AS c,
	category_name AS cat
FROM pois, a WHERE current_zoom >= 14 -- real data from level 14 and above

This technique is useful on other use cases when we want to change the geometries based on the zoom level!!


The new way™


Overviews at our platform

  • Big datasets will be automatically detected
  • CDB_CreateOverviews('table') to start the overviews generation (tweak statement_timeout and maybe go to psql)
  • Look for _vovw_ZOOM_LEVEL_ tables or use CDB_Overviews('table'::regclass) function
  • Remove with CDB_DropOverviews('table'::regclass)
  • Maps API will use it automatically (code)
  • This change the way we use symbology (comp-op's have no effect on overviews)

More info here


On the editor

Run this to create overviews on your new table.

set statement_timeount=9999999;
SELECT
    CDB_CreateOverviews('table_name');

Demo

<iframe src="https://team.cartodb.com/u/jsanz/viz/2d2dbc20-0b3f-11e6-abc3-0ecd1babdde5/embed_map" frameborder="0" style="width:100%;height:500px;" > </iframe>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment