...
Code Block |
---|
LAYER NAME "ward_lookup" STATUS OFF TYPE POLYGON DATA "Overlays/ward_lookup.shp" TOLERANCE 0 METADATA qstring_validation_pattern "." qstr_validation_pattern "." END LABELITEM "name" CLASS NAME "" STYLE OUTLINECOLOR 81 167 174 WIDTH 3 END LABEL FONT "verdana" TYPE truetype SIZE 8 COLOR 255 0 0 OUTLINECOLOR 255 255 255 OUTLINEWIDTH 3 END END END |
Of particular interest is:
...
Code Block |
---|
LAYER NAME "ward_summary" STATUS OFF TYPE POINT INCLUDE "_datashare.map" DATA "wkb_geometry from (select ogc_fid, code, name, ST_PointOnSurface(wkb_geometry) as wkb_geometry from boundaryline.wards_surrey) as foo using unique ogc_fid using srid=27700" TOLERANCE 0 METADATA qstring_validation_pattern "." qstr_validation_pattern "." END CLASS NAME "" STYLE SYMBOL "circle" COLOR 0 128 0 SIZE 8 END END END |
...
Of note is the SQL used in the DATA
statement which creates a row per ward with a point within each ward polygon which will be used for the lookup. The PostGIS function ST_PointOnSurface
returns a POINT
guaranteed to lie on the surface of a polygon.
...
The My Ward page now simply displays the name of the ward that the current address falls within, not very excited but a start.
Adding a list of planning applications
...
Studio - Workflow
- Create a new Stored Procedure Task called
Create ward_planning_summary
, choose:- Database:
DataShare
- Function:
at_sys_create_table
from the Astun category (from v6.0.0 this is called wkf_create_table under the -Workflow- Function filter) - Set the tablename parameter to:
ward_planning_summary
and the selectstatement to:
- Database:
Code Block | ||
---|---|---|
| ||
WITH counts AS ( SELECT wards.code, count(*) AS count FROM planning.surrey_planning planning, boundaryline.wards_surrey wards WHERE st_intersects(wards.wkb_geometry, planning.wkb_geometry) GROUP BY wards.code ) SELECT wards.ogc_fid, wards.code, st_pointonsurface(wards.wkb_geometry) AS wkb_geometry, 'There ' || CASE WHEN counts.count = 1 THEN 'is ' ELSE 'are ' END || coalesce(counts.count::text, 'no') || ' planning application' || CASE WHEN counts.count = 1 THEN '' ELSE 's' END AS summary FROM boundaryline.wards_surrey wards LEFT JOIN counts ON (wards.code = counts.code) |
...
Create a
ward_planning_summary
layer inoverlays.map
...
Code Block |
---|
LAYER NAME "ward_planning_summary" STATUS OFF TYPE POINT INCLUDE "_datashare.map" DATA "wkb_geometry from (select * from ward_planning_summary) as foo using unique ogc_fid using srid=27700" TOLERANCE 0 METADATA qstring_validation_pattern "." qstr_validation_pattern "." END CLASS NAME "" STYLE SYMBOL "circle" COLOR 0 128 0 SIZE 8 END END END |
Studio - MapSource
- Create a
Planning summary
layer below theSummary
group- Select the
ward_planning_summary
Layer Name - Ensure the Show MySearch and Show My option is checked
- Choose the
summary
field
- Select the
Create a ward map
Currently all data is shown in My Nearest panels but lets add a map showing the current ward then update the page style via CSS to display the map and summary panels side by side at the top of the page followed by all other panels as My Nearest panels below.
Studio - Workflow
First we're going to create a table which has a row per ward with a POINT
within the ward and a html
field containing an img
element with the appropriate src
URL to generate a map zoomed to the extent of the ward.
- Create a new Stored Procedure Task called
Create ward_map
, choose:- Database:
DataShare
- Function:
at_sys_create_table
from the Astun categorycategory (from v6.0.0 this is called wkf_create_table under the -Workflow- Function filter) - Set the tablename parameter to:
ward_map
and the selectstatement to:
- Database:
Code Block | ||
---|---|---|
| ||
WITH envelope AS ( SELECT ogc_fid, wkb_geometry, code, name, st_expand(st_envelope(wkb_geometry), 300) AS envelope FROM boundaryline.wards_surrey wards ), coords AS ( SELECT *, st_x(st_centroid(envelope.envelope))::int AS x, st_y(st_centroid(envelope.envelope))::int AS y, greatest((st_xmax(envelope) - st_xmin(envelope)), (st_ymax(envelope) - st_ymin(envelope)))::int AS z FROM envelope ) SELECT ogc_fid, code, name, st_pointonsurface(wkb_geometry) AS wkb_geometry, '<img class="map" src="MapGetImage.aspx?RequestType=Map&MapWidth=450&MapHeight=450&MapSource=Astun/Default&Easting=' || x || '&Northing=' || y || '&Zoom=' || z || '&mapid=-1&ServiceAction=ZoomToLocation&Layers=ward_lookup,ward_summary" alt="' || name || '" />' AS html FROM coords |
The SQL first creates an envelope
result set which includes a geometry for the bounds of each ward, the coords
result set then extracts the centre (x
/y
) and width (z
) and finally a result set uses these values to construct a POINT
within the ward and a html
field containing anan img
element with the appropriate src
URL to generate a map zoomed to the extent of the ward. The URL specifies that the ward_lookup
layer is displayed on the map so that the boundaries are visible together with labels.
Mapfile
Code Block |
---|
LAYER NAME "ward_map" STATUS OFF TYPE POINT INCLUDE "_datashare.map" DATA "wkb_geometry from (select *, html as html_raw from ward_map) as foo using unique ogc_fid using srid=27700" TOLERANCE 0 METADATA qstring_validation_pattern "." qstr_validation_pattern "." END CLASS NAME "" STYLE SYMBOL "circle" COLOR 0 128 0 SIZE 8 END END END |
Of note is the SQL used in the DATA
statement which creates html_raw
column by aliasing the existing html
column. This allows us to use the html_raw
column for the MapSource layer causing the raw HTML text to be output which will cause the img
to be displayed.
Studio - MapSource
- Create a
Ward map
Layer Group- Ensure Visible on Startup is checked
- Move it up so it's the first Layer Group with the
Summary
Layer Group second
- Create a
Ward map
Layer- Select the
ward_map
Layer Name - Ensure the Show MySearch and Show My option is checked
- Choose the
html_raw
field and ensure Display field name is unchecked
- Select the
Styling
To display the map and summary panels side by side at the top of the page we need a little JavaScript and CSS.
JavaScript
In order to allow us identify the first and second panels on the My Ward page we will use a little JavaScript to:
- Add a class to the page body element to allow us to identify which page is currently being displayed
- Add a class to each panel with it's position in the page
- Disable ability to collapse the first two panels
Code Block | ||
---|---|---|
| ||
window.astun.customJS = function() { // Determine which page we are on from the selected tab var pageName = jQuery('.atTabSelected').val().replace(/\s/, '_').toLowerCase(); // Add a class to the body to allow us to create specific rules for individual pages jQuery('body').addClass(pageName); // Add a class to each panel with it's position in the page, // atPanel0, atPanel1 etc. jQuery('.atPanelContainer .atPanel').each(function(idx) { jQuery(this).addClass('atPanel' + idx); }); if (pageName === 'my_ward') { // Make the first two panels on the My Ward page non-collapsable jQuery('.atPanel.atPanel0, .atPanel.atPanel1').find("*").unbind(); // Remove the "Click to toggle" tooltip jQuery('.atPanel.atPanel0, .atPanel.atPanel1').find(".atPanelHeader").attr("title", ""); } }; |
CSS
The following CSS when added to one of the site specific CSS files found under the Web\custom
directory applies the following styles:
- Display the first two panel side by side and always expanded
- Hide the headings for the summary panel
- Hide the "Details" and "Distance" table headings
- Make the ward map responsive
Code Block | ||
---|---|---|
| ||
/* Position the first two My Ward panels */ .my_ward .atPanel.atPanel0, .my_ward .atPanel.atPanel1 { width: 48%; float:left; margin-left: 10px; } /* Always display the content of the first two panels */ .my_ward .atPanel.atPanel0 .atPanelParentContent, .my_ward .atPanel.atPanel1 .atPanelParentContent { display: block !important; } /* Ensure the third panel displays below the first two */ .my_ward .atPanel.atPanel0, .my_ward .atPanel.atPanel2 { clear: both; } /* Style headers for the first two panels */ .my_ward .atPanel.atPanel0 .atPanelHeader, .my_ward .atPanel.atPanel1 .atPanelHeader { cursor: default !important; } .my_ward .atPanel.atPanel0 .atPanelStatusSign, .my_ward .atPanel.atPanel1 .atPanelStatusSign { visibility: hidden; } /* Style panel content */ /* Hide layer titles for first two panels */ .my_ward .atPanel.atPanel0 h4, .my_ward .atPanel.atPanel1 h4 { display: none; } .my_ward table thead { display: none; } .my_ward table tr th:nth-child(1), .my_ward table tr td:nth-child(1) { width: 100%; } /* Make the ward map responsive */ .my_ward img.map { width: 100%; } |
Responsive design
As a bonus lets make some small changes to make the My Ward responsive and display appropriately on a range of device sizes. In this case we can simply add a single media query to our CSS to apply a different style to the first two panels when the screen width is smaller than a given size.
Code Block | ||
---|---|---|
| ||
@media (max-width: 640px) { .my_ward .atPanel.atPanel0, .my_ward .atPanel.atPanel1 { width: 100%; margin-left: 0; } } |