'Draw sector by center point, radius, interior angle and azymuth in postgis

I'm building a javascript application using Bing Maps API, and I would like to make up sector geometries from center points and sector parameters.

I have a table 'points' in PostgreSQL database with PostGIS on top and it contains point geometries:

id  st_astext(geom)
1   POINT(4.331 50.869)
2   POINT(4.323 50.832)
3   POINT(4.373 50.853)
4   POINT(4.356 50.837)

I have another table 'segemnts' where each entry has following attributes: azimuth (in degrees), beamwidth (in degrees), range (in meters) and centerid that is basically foreign key to 'points' table:

centerid    azimuth beamwidth   range
1           210     60          750
2           135     30          500
3           80      60          600
4           165     90          750

How do I get a table or a view in my database to select circle segments that have above points as a center point, range as radius, beamwidth as interior angle, and azimuth as a direction?

enter image description here



Solution 1:[1]

You may employ st_buffer() to get circle of given radius around your points, than build triangle with interior angle equal to your beamwidth, using st_project(), than you may intersect those to get sector geometries, like:

create view sectors as 
select s1.*, st_intersection(st_buffer(p1.geom::geography, s1.range, 50)::geometry, st_makepolygon(st_makeline(array[p1.geom, st_project(p1.geom::geography, s1.range*2, radians(s1.azimuth-s1.beam/2))::geometry, st_project(p1.geom::geography, s1.range*2, radians(s1.azimuth+s1.beam/2))::geometry, p1.geom]))) as geom
from sector s1
left join points p1
on p1.id=s1.centerid

Pay attention, I have used type casting between geography and geometry to match expected function parameters, also I have multiplied s1.range by two, so it is big enough to cut whole sector and I used 50 as a third st_buffer() parameter so you sector is smooth enough.

Your sample data renders into something, like:

enter image description here

Solution 2:[2]

Building on the answer from @user3307073 and extending it to handle angles greater or equal to 180 degrees.

First some helper functions:

normalizes angle in radians to (-pi,pi)

create or replace function misc_normalizeRadians(rad double precision)
returns double precision
as $$
    SELECT r-floor(r/pi())*(2*pi()) FROM (SELECT (floor($1/(2*pi()))*-(2*pi())+$1) as r) as t;
$$ LANGUAGE sql immutable;

Rotates a 2D heading with an angle in radians

create or replace function misc_rotateHeadingByAngle(heading double precision,angle double precision)
returns double precision
as $$
    select misc_normalizeRadians($1-$2);
$$ LANGUAGE sql immutable;

Builds a triangle specifying origin,heading, inner angle and length of sides

create or replace function util_buildCone(origin geometry,bearing double precision,angle double precision,sides double precision)
returns geometry
as $$
BEGIN
    IF ST_GeometryType($1)!='ST_Point' THEN
         RAISE EXCEPTION 'Function only well defined for points, got: %',ST_GeometryType($1);
    END IF;
    IF abs($3)>=pi() THEN
         RAISE EXCEPTION 'Cones can''t have interior angle greater or equal to half a rotation, got: %',$3;
    END IF;
    RETURN (select ST_MakePolygon(ST_MakeLine(ARRAY[$1,a,b,$1]))
            from
                 util_translateTowardsBearing($1,misc_rotateHeadingByAngle($2,$3/2),$4) as a,
                 util_translateTowardsBearing($1,misc_rotateHeadingByAngle($2,-$3/2),$4) as b);
END
$$ language plpgsql immutable;

Builds a circle sector given origin, heading, iner angle and radius

create or replace function buildSector(origin geometry,bearing double precision,angle double precision,sides double precision)
returns geometry
as $$
BEGIN
    IF ST_GeometryType($1)!='ST_Point' THEN
        RAISE EXCEPTION 'Function only well defined for points, got: %',ST_GeometryType($1);
    END IF;
    IF abs($3)>(2*pi()) THEN
        RAISE EXCEPTION 'Cones can''t have a sector greater than the whole circle, got : %',$3;
    END IF;
    IF abs($3)=(2*pi()) THEN
        RETURN (select ST_Buffer($1,$4,50));
    END IF;
    IF abs($3)>(pi()/2) THEN
        RETURN (select  ST_Union(a,ST_Snap(b,a,$4/10000))
                from    buildSector($1,misc_rotateHeadingByAngle($2,$3/4),$3/2,$4) as a,
                        buildSector($1,misc_rotateHeadingByAngle($2,-$3/4),$3/2,$4) as b);
    END IF;
    RETURN (select ST_Intersection(ST_Buffer($1,$4,50),util_buildCone($1,$2,$3,$4*2)));
END
$$ language plpgsql immutable;

Then creating the view:

create view sectors as 
select s1.*, buildSector(p1.geom,s1.azimuth,s1.beam,s1.range) as geom
from sector s1 left join points p1 on p1.id=s1.centerid

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 lijat