Skip to content

Instantly share code, notes, and snippets.

@andrewxhill
Last active August 29, 2015 14:04
Show Gist options
  • Select an option

  • Save andrewxhill/57b5bfc3e254a8409a8b to your computer and use it in GitHub Desktop.

Select an option

Save andrewxhill/57b5bfc3e254a8409a8b to your computer and use it in GitHub Desktop.
PlpgSQL to cross dateline when drawing two points
-- See it on a map:
--
-- with a as (
-- SELECT
-- axh_crossdateline(the_geom_webmercator ORDER BY cartodb_id DESC) geom
-- FROM
-- deletetest
-- )
-- SELECT geom AS the_geom_webmercator from a
--
-- OR, write it back to a new table:
--
-- with a as (
-- SELECT
-- axh_crossdateline(the_geom_webmercator ORDER BY cartodb_id DESC) geom
-- FROM
-- deletetest
-- )
-- SELECT ST_Transform(geom,4326) AS the_geom from a
CREATE AGGREGATE axh_crossdateline (geometry)
(
sfunc = axh_crossdateline_fn,
stype = geometry
);
CREATE OR REPLACE FUNCTION AXH_CrossDateLine_fn (oldg GEOMETRY, newg GEOMETRY)
RETURNS geometry as $$
DECLARE
westmost GEOMETRY;
eastmost GEOMETRY;
curstr GEOMETRY;
endpoint GEOMETRY;
newseg GEOMETRY := NULL;
h FLOAT8;
p FLOAT8;
zy FLOAT8;
i INT;
dir INT := 1;
BEGIN
IF newg IS NULL THEN
RETURN oldg;
END IF;
IF oldg IS NULL THEN
IF newg IS NULL THEN
RETURN NULL;
END IF;
RETURN newg;
END IF;
IF ST_SRID(newg) != 3857 THEN
RAISE EXCEPTION 'Expects the_geom_webmercator (SRID:3857) as input geometry';
END IF;
IF ST_GeometryType(oldg) = 'ST_MultiLineString' THEN
curstr := ST_GeometryN(oldg, ST_NumGeometries(oldg));
ELSE
curstr := oldg;
END IF;
endpoint := ST_PointN(curstr, ST_NumPoints(curstr));
IF ABS(ST_X(ST_Transform(newg,4326)) - ST_X(ST_Transform(endpoint,4326))) > 180 THEN
IF ST_X(endpoint) < ST_X(newg) THEN
westmost := endpoint;
eastmost := newg;
ELSE
westmost := newg;
eastmost := endpoint;
dir := 2;
END IF;
h := GREATEST(ST_Y(eastmost), ST_Y(westmost)) - LEAST(ST_Y(eastmost), ST_Y(westmost));
p := (h / (20037508.34 - ST_X(eastmost) + ST_X(westmost) + 20037508.34 )) * (20037508.34 - ST_X(eastmost));
zy := LEAST(ST_Y(eastmost), ST_Y(westmost)) + p;
IF dir = 2 THEN
curstr := ST_MakeLine(
curstr,
ST_MakeLine(eastmost, ST_SetSRID(ST_MakePoint(20037508.34, zy),3857))
);
newseg := ST_MakeLine(ST_SetSRID(ST_MakePoint(-20037508.34, zy),3857), westmost);
ELSE
curstr := ST_MakeLine(
curstr,
ST_MakeLine(westmost, ST_SetSRID(ST_MakePoint(-20037508.34, zy),3857))
);
newseg := ST_MakeLine(ST_SetSRID(ST_MakePoint(20037508.34, zy),3857), eastmost);
END IF;
ELSE
curstr := ST_MakeLine(curstr, newg);
END IF;
IF ST_GeometryType(oldg) = 'ST_MultiLineString' THEN
--put the multi back together
IF ST_NumGeometries(oldg) > 1 THEN
i := 2;
eastmost := ST_GeometryN(oldg, 1);
WHILE i < ST_NumGeometries(oldg) LOOP
eastmost := ST_Collect(eastmost, ST_GeometryN(oldg, i));
i := i+1;
END LOOP;
curstr := ST_Collect(eastmost, curstr);
END IF;
END IF;
-- Add the latest segment if there was one
IF newseg IS NOT NULL THEN
curstr := ST_Collect(curstr, newseg);
END IF;
RETURN curstr;
END;
$$ language plpgsql IMMUTABLE;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment