Skip to content

Instantly share code, notes, and snippets.

@fitnr
Forked from stuartlynn/query.sql
Last active November 8, 2016 16:20
Show Gist options
  • Save fitnr/74a3effcaee0d5751c4959c4c2f23965 to your computer and use it in GitHub Desktop.
Save fitnr/74a3effcaee0d5751c4959c4c2f23965 to your computer and use it in GitHub Desktop.

Dasymetric Dot Density Mapping for MCNY

All of the code you need to reproduce it is in this gist. You will need to create both the weighted_dot_density.sql and select_random_weights.sql functions in your Carto account (just copy and past them in to the SQL terminal) 

Then you run the query once for each ethnicity which gives you 4 tables with dot density in each for each ethnicity. Oh and after you run the create table queries you need to run 

select cdb_cartodbfytable('account_name', 'table_name') to have it show up in your datasts

You can then ether union them together to make one table or just create 4 layers on the map. You will want to do this through the batch API, I can help you write some scripts to do that if you like. Also the batch API docs are here:

Really happy with the result: https://team.carto.com/u/stuartlynn/builder/8da6e484-6f8d-11e6-a470-0ecd1babdde5/embed

Its a little slow cause its having to render a huge number of points but the tiles should all cache so it should be quick unless you update something.

The Carto css we are using is 

#layer {
  marker-width: 0.2;
  marker-fill: #0B9F95;
  marker-fill-opacity: 0.9;
  marker-allow-overlap: true;
  marker-line-width: 0;
  marker-line-color: #fff;
  marker-line-opacity: 1;
}

with the colors:

hispanic: #0B9F95
asian: #FF528C
black: #FF8A00
white: #41599C
CREATE OR REPLACE FUNCTION cdb_dot_density(g geometry , no_points Integer, max_iter Integer DEFAULT 1000 )
RETURNS setof geometry AS
$$
DECLARE
extent GEOMETRY;
test_point Geometry;
width NUMERIC;
height NUMERIC;
x0 NUMERIC;
y0 NUMERIC;
xp NUMERIC;
yp NUMERIC;
no_left INTEGER;
points GEOMETRY[];
BEGIN
extent := ST_Envelope(g);
width := ST_XMax(extent) - ST_XMIN(extent);
height := ST_YMax(extent) - ST_YMIN(extent);
x0 := ST_XMin(extent);
y0 := ST_YMin(extent);
no_left := no_points;
LOOP
if(no_left=0) THEN
EXIT;
END IF;
xp = x0 + width*random();
yp = y0 + height*random();
test_point = CDB_LATLNG(yp,xp);
IF(ST_Contains(g, test_point)) THEN
no_left = no_left - 1;
RETURN NEXT test_point;
END IF;
END LOOP;
END
$$
LANGUAGE plpgsql VOLATILE
CREATE TABLE ADD_white AS (
WITH one AS (
SELECT pluto.the_geom,
white_noth,
asian_noth,
black_noth,
hl,
LT_201_Race.cartodb_id,
-- resarea,
-- ST_AREA(
-- ST_INTERSECTION(pluto.the_geom, LT_201_Race.the_geom)
-- ) / ST_AREA(pluto.the_geom) AS area_frac,
ST_AREA(
ST_INTERSECTION(pluto.the_geom, LT_201_Race.the_geom)
) / ST_AREA(pluto.the_geom) * resarea AS live_area
FROM LT_201_Race,
nyc_pluto_15v1_resonly AS pluto
WHERE ST_INTERSECTS(pluto.the_geom, LT_201_Race.the_geom)
),
totals AS (
SELECT
SUM(live_area) AS total_live_area,
cartodb_id
FROM one
GROUP BY cartodb_id
),
fracs AS (
SELECT totals.total_live_area,
one.*,
one.live_area / totals.total_live_area AS frac
FROM one,
totals
WHERE totals.cartodb_id = one.cartodb_id
)
SELECT
weighted_DD(
array_agg(the_geom),
min(white_noth)::NUMERIC,
array_agg(frac)::NUMERIC[]
) AS the_geom
FROM fracs
GROUP BY cartodb_id
)
create or replace function select_random_weights(array_ids numeric[], weights numeric[]) returns NUMERIC
as $$
DECLARE
result NUMERIC;
BEGIN
WITH idw as (
select unnest(array_ids) as id, unnest(weights) as percent
),
CTE AS (
SELECT random() * (SELECT SUM(percent) FROM idw) R
)
SELECT *
FROM (
SELECT id, SUM(percent) OVER (ORDER BY id) S, R
FROM idw as percent CROSS JOIN CTE
) Q
WHERE S >= R
ORDER BY id
LIMIT 1
into result;
return result;
END
$$ LANGUAGE plpgsql
CREATE OR REPLACE FUNCTION stuartlynn.weighted_dd(geoms geometry[], no_points numeric, weights numeric[])
RETURNS SETOF geometry
LANGUAGE plpgsql
AS $function$
DECLARE
i NUMERIC;
ids NUMERIC[];
selected_poly NUMERIC;
p GEOMETRY;
BEGIN
with idseries as (
select generate_series(1,array_upper(geoms,1)) as id
)
select array_agg( id) from idseries into ids;
FOR i in 1..no_points
LOOP
select select_random_weights(ids, weights) into selected_poly;
select cdb_dot_density(geoms[selected_poly], 1) into p;
return next p;
END LOOP;
END
$function$
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment