-- Function: fn_offsetpoint(geometry, double precision, double precision, character)

-- DROP FUNCTION fn_offsetpoint(geometry, double precision, double precision, character);

CREATE OR REPLACE FUNCTION fn_offsetpoint(line geometry, distance double precision, "offset" double precision, side character)
  RETURNS geometry AS
$BODY$
	/*
		Return a point of distance along line (in percent 0 to 1) and offset by "offset" to the left or right of the line segment
	*/
	--DECLARE nline geometry;
	DECLARE nPoints int;
	DECLARE i int;
	DECLARE	dx double precision;
	DECLARE	dy double precision;

	DECLARE	x1 double precision;
	DECLARE	y1 double precision;
	DECLARE	x2 double precision;
	DECLARE	y2 double precision;

	DECLARE totalDist double precision;
	DECLARE lineLength double precision;
	DECLARE segLength double precision;
	DECLARE pnt geometry;
BEGIN 

	IF ( (GeometryType(line) != 'LINESTRING'::text) AND (GeometryType(line) != 'MULTILINESTRING'::text) ) THEN
		RETURN 'POINT(0 0)'::geometry;
	END IF;

	/*
		Walk the line and find the segment that this point lies on
	*/
	nPoints := st_npoints(line);
	lineLength := st_length(line);
	segLength := 0;
	totalDist := 0;
	FOR i in 1..(nPoints-1) LOOP
		--Grab the points from this segment
		x1=st_x(PointN(line,i));
		x2=st_x(PointN(line,i + 1));
		y1=st_y(PointN(line,i));
		y2=st_y(PointN(line,i + 1));
		--Add distance of segment to running total
		segLength := sqrt( ( (x2-x1) ^ 2  ) + ( (y2-y1) ^ 2) );
		totalDist := totalDist + segLength;
		--Are we beyond the percentage specified? then exit loop with this segment
		if ( (totalDist / lineLength) >= distance) THEN EXIT; END IF;
	END LOOP;

	IF (side = 'L') THEN
		dy := (x2-x1) / segLength;
		dx := -(y2-y1) / segLength;
	ELSE
		dy := -(x2-x1) / segLength;
		dx := (y2-y1) / segLength;
	END IF;

	dx := dx * offset;
	dy := dy * offset;
	pnt := line_interpolate_point(line,distance);

	/*
		offset the point
	*/
	RETURN st_translate(pnt, dx, dy);
END;
$BODY$
  LANGUAGE 'plpgsql' IMMUTABLE
  COST 100;
ALTER FUNCTION fn_offsetpoint(geometry, double precision, double precision, character) OWNER TO postgres;
