MS Access Front End – Linked to PostGreSQL back end – a simple walk through using Access 2003

As I have indicated before MS Access makes a brilliant ETL tool. Important in this is being able to connect to different databases. I have set out how to connect to MySQL and SQLAzure before – the following sets out how to connect to PostGres.

To follow along you will either need PostGres installed on your local computer or alternatively all required connection parameters to a database in the cloud or on your lan.
PostGres Download

You will also need to Download and install a PostGreSQL ODBC driver – these are available at the following(March 2018).

PostGres ODBC Drivers

Scroll down the list – here I went to the bottom and obtained x64 version – MSI are downloaded (Microsoft Windows Installer files) – Install and then move to the next step.

Next create a blank database and right click in the white area to reveal a menu – select link tables.

You should now be presented with the Select Data Source dialog. Here I hit New…

This brings up the Create New Data Source dialog which lists database drivers scroll down through the list to PostgreSQL Unicode and select

Give your DSN an appropriate name and then go back to link table but this time rather than hitting the new button navigate to where you saved your DSN and select it and press OK.

The ever important parameters – you just need to know these – if you set up PostGres and you can get in through PG Admin selecting properties on the database should reveal the panel that will give you some guidelines. You may wish to double click on the image below so you can get a closer link of how I place my parameters in. I have opened the PGAdmin dialog here and placed it alongside the MS Access database window to show the properties I am transferring across.

Hitting OK should present you with the tables in your database.

And here is a demonstration with the link in place along with the table open and a simple form shown.

MS Access Function Collection that can be used to Generate Housing Forecast Figures

Apologies if you are coming here for the first time. This post is a somewhat dense domain specific holding post for some work I did at the weekend to pull together some thoughts.

I was thinking that if I had a list of all sites in the UK I should be able to generate a phasing for each of them based on maybe the area of the site. This would automatically create a general housing land audit for any site that I should put in. I thought I’d try and see if I could write a set of functions that might generate phasing by site. Given that you can get an estimate of the housing boundaries from open street map and that you can get their area it would be possible to get an estimated number of houses per site which could then be used to phase. Going further if you were ever to know planning permission dates you could more accurately use this as a date from which to start phasing.

Ensure you have three tables
T01Sites (PKID, TotalNoHouses, DecisionDate)
T02HousePhasing (PKID, SiteFKID, Year, Completions)
T03 (PKID, TotalNoHouses, DecisionDate, YearofDecision, YearofStart, YearSpread)

T02 is the phasing child table of T01 and T03 is a holding table for a make table that will hold a randomised spread over which you wish to phase the total no of houses. It also randomly predicts when the housing will start on site.

And three queries

Q01 – Make Table Query

SELECT T01Sites.PKID, T01Sites.SiteName, T01Sites.TotalNoHouses, T01Sites.DecisionDate, Year([DecisionDate]) AS YearofDecision, CalculateintYearStartFULLPP([YearofDecision]) AS YearofStart, intYearSpread([TotalNoHouses]) AS YearSpread INTO T03
FROM T01Sites;

Q02 – Select Query

SELECT T03.PKID, T03.SiteName, T03.TotalNoHouses, T03.DecisionDate, T03.YearofStart, T03.YearSpread, Int(T03!TotalNoHouses/T03!YearSpread) AS PerYearSpread, [TotalNoHouses] Mod [YearSpread] AS Remainder
FROM T03
WHERE ((([TotalNoHouses] Mod [YearSpread])=0));

Q03 – Select Query

SELECT T03.PKID, T03.SiteName, T03.TotalNoHouses, T03.DecisionDate, T03.YearofStart, T03.YearSpread, Int(T03!TotalNoHouses/T03!YearSpread) AS PerYearSpread, [TotalNoHouses] Mod [YearSpread] AS Remainder
FROM T03
WHERE ((([TotalNoHouses] Mod [YearSpread])>0));

VBA Function list
The first function randomises the spread in years over which construction might happen on an individual housing site based on the total number of houses on the site.

Public Function intYearSpread(TotalNoHouses As Integer) As Integer

If TotalNoHouses < 2 Then

intYearSpread = 1

ElseIf TotalNoHouses = 2 Then

intYearSpread = Int((TotalNoHouses) * Rnd) + 1

ElseIf TotalNoHouses > 2 And TotalNoHouses < 9 Then

intYearSpread = Int((TotalNoHouses - 2 + 1) * Rnd + 1)

ElseIf TotalNoHouses >= 9 And TotalNoHouses <= 40 Then

intYearSpread = Int((4) * Rnd + 1)

ElseIf TotalNoHouses >= 41 And TotalNoHouses <= 80 Then

intYearSpread = Int((8 - 4 + 1) * Rnd + 4)

ElseIf TotalNoHouses >= 81 And TotalNoHouses <= 200 Then

intYearSpread = Int((8 - 4 + 1) * Rnd + 4)

ElseIf TotalNoHouses >= 201 And TotalNoHouses <= 400 Then

intYearSpread = Int((10 - 6 + 1) * Rnd + 6)

ElseIf TotalNoHouses >= 401 And TotalNoHouses <= 800 Then

intYearSpread = Int((12 - 8 + 1) * Rnd + 8)

Else

intYearSpread = Int((20 - 10 + 1) * Rnd + 10)

End If

'MsgBox intYearSpread

End Function

The first of the next three functions is used in the query to identify a year from which phasing on site will start. I wrote two further functions with the thought that in the future I could create a switch that would allow alternative site starts depending on whether a site has planning permission and depending on the type of planning permission. For example full planning permission would mean starting within three years of the granting of planning permission whereas outline would push it to between 3 and 6 years. A site with an LDP allocation would start further into the future.

Public Function CalculateintYearStartFULLPP(intDecisionDateYear As Integer) As Integer

CalculateintYearStartFULLPP = intDecisionDateYear + (Int((3 - 1 + 1) * Rnd + 1))

End Function

'Not used at present
Public Function CalculateintYearStartPPPP(intDecisionDateYear As Integer) As Integer

CalculateintYearStartPPPP = intDecisionDateYear + (Int((6 - 3 + 1) * Rnd + 3))

End Function

'Not used at present
Public Function CalculateintYearStartLDP(intDecisionDateYear As Integer) As Integer

CalculateintYearStartLDP = intDecisionDateYear + (Int((20 - 8 + 1) * Rnd + 8))

MsgBox CalculateintYearStartLDP

End Function

Function to create phasing IF housing IS perfectly divisible by Year Spread
GRH is an acronym for Generate Randomised Housing

Public Function GRHZero() As Variant

Dim db As DAO.Database
Dim rsSource As DAO.Recordset
Dim rsPhasing As DAO.Recordset
Dim intrsSourcePKID As Integer
Dim intrsSourceYearofStart As Integer
Dim intYearSpread As Integer
Dim intPerYearSpread As Integer
Dim i As Integer

Set db = CurrentDb()
Set rsSource = db.OpenRecordset("Q02")
Set rsPhasing = db.OpenRecordset("T02HousePhasing")

If Not (rsSource.EOF And rsSource.BOF) Then
'There are no records if End of File and Beginning of File are both true

rsSource.MoveFirst

Do Until rsSource.EOF = True

intrsSourcePKID = rsSource!PKID
intrsSourceYearofStart = rsSource!YearofStart
intYearSpread = rsSource!YearSpread
intPerYearSpread = rsSource!PerYearSpread

For i = 1 To intYearSpread

With rsPhasing
rsPhasing.AddNew
rsPhasing!SiteFKID = intrsSourcePKID
rsPhasing!Year = intrsSourceYearofStart
rsPhasing!Completions = intPerYearSpread
rsPhasing.Update
intrsSourceYearofStart = intrsSourceYearofStart + 1
End With

Next i

rsSource.MoveNext

Loop
Else
MsgBox "No Records"
Exit Function
End If

rsPhasing.Close
rsSource.Close

Set rsPhasing = Nothing
Set rsSource = Nothing

Set db = Nothing

End Function

Function to create phasing IF housing IS NOT perfectly divisible by Year Spread and a remainder is put on end

Public Function GRHRemainder() As Variant

Dim db As DAO.Database
Dim rsSource As DAO.Recordset
Dim rsPhasing As DAO.Recordset
Dim intrsSourcePKID As Integer
Dim intrsSourceYearofStart As Integer
Dim intYearSpread As Integer
Dim intPerYearSpread As Integer
Dim intRemainder As Integer
Dim i As Integer

Set db = CurrentDb()
Set rsSource = db.OpenRecordset("Q03")
Set rsPhasing = db.OpenRecordset("T02HousePhasing")

If Not (rsSource.EOF And rsSource.BOF) Then
'There are no records if End of File and Beginning of File are both true

rsSource.MoveFirst

Do Until rsSource.EOF = True

intrsSourcePKID = rsSource!PKID
intrsSourceYearofStart = rsSource!YearofStart
intYearSpread = rsSource!YearSpread
intPerYearSpread = rsSource!PerYearSpread
intRemainder = rsSource!Remainder

For i = 1 To intYearSpread

With rsPhasing
rsPhasing.AddNew
rsPhasing!SiteFKID = intrsSourcePKID
rsPhasing!Year = intrsSourceYearofStart
rsPhasing!Completions = intPerYearSpread
rsPhasing.Update
intrsSourceYearofStart = intrsSourceYearofStart + 1
End With

Next i

With rsPhasing
rsPhasing.AddNew
rsPhasing!SiteFKID = intrsSourcePKID
rsPhasing!Year = intrsSourceYearofStart
rsPhasing!Completions = intRemainder
rsPhasing.Update
intrsSourceYearofStart = intrsSourceYearofStart + 1
End With

rsSource.MoveNext

Loop
Else
MsgBox "No Records"
Exit Function
End If

rsPhasing.Close
rsSource.Close

Set rsPhasing = Nothing
Set rsSource = Nothing

Set db = Nothing

End Function

And the Script to run both the above functions

Public Function GeneratePhasingRecords()

Call GRHZero
Call GRHRemainder

MsgBox "Finished"

End Function

AHK – Useful AutoHotKeyScripts for Outlook specifically

Another set of very useful scripts mainly dealing with creating hotkeys for common Outlook tasks.

Note that if you are on a laptop you may have pre-mappings for the function keys in which case you will need to quite possibly go for alternatives.

F8::
Run C:\Program Files (x86)\Microsoft Office\Office14\OUTLOOK.EXE /c ipm.note
return

F9:: ;Inbox
keystroke = ^1
parameters = 
Gosub open
Return

F10:: ;Calender
keystroke = ^2
parameters =  "outlook:calendar"
Gosub open
Return

F11:: ;Contacts
keystroke = ^3
parameters = outlook:contacts
Gosub open
Return


F12::
PROCESS, EXIST, OUTLOOK.EXE
PID := ERRORLEVEL
IF ERRORLEVEL <> 0
{
       LOOP,
      {
            WINSHOW, AHK_CLASS rctrl_renwnd32 
            WINACTIVATE, AHK_CLASS rctrl_renwnd32 
            WINWAITACTIVE, AHK_PID %PID% AHK_CLASS rctrl_renwnd32
            WINGETACTIVETITLE, TITLE
            WINCLOSE, %TITLE%
            WINWAIT,, Are you sure you want to permanently delete all the items and subfolders in the "Deleted Items" folder?,3
            CONTROLSEND, , {ENTER}, AHK_CLASS #32770, Are you sure you want to permanently delete all the items and subfolders in the "Deleted Items" folder?
            IF A_INDEX > 30
                  PROCESS, CLOSE, OUTLOOK.EXE

            PROCESS, EXIST, OUTLOOK.EXE
            IF ERRORLEVEL = 0
                  BREAK
      }
}
return

DetectHiddenWindows, On
Process, Exist, outlook.exe
If !ErrorLevel
   Run outlook.exe
Return

open:
Process, Exist, outlook.exe
If (ErrorLevel != 0)
{
	WinActivate ahk_class rctrl_renwnd32
	WinWaitActive ahk_class rctrl_renwnd32
	Send %keystroke%
}
else
	Run outlook.exe %parameters%
Return

AHK – Useful AutoHotKeyScripts

F2::
; Close all windows (open/minimized, browsers) but not pwr off
	WinGet, id, list,,, Program Manager
	Loop, %id%
	{
	this_id := id%A_Index% 
	WinActivate, ahk_id %this_id%
    	WinGetClass, this_class, ahk_id %this_id%
	WinGetTitle, this_title, ahk_id %this_id%
	If(This_class != "Shell_traywnd") && (This_class != "Button")  ; If class is not Shell_traywnd and not Button
	WinClose, ahk_id %this_id% ;This is what it should be ;MsgBox, This ahk_id %this_id% ; Easier to test ;)
	}
Return

If you don’t know or can’t find the executable for the program you wish to AHK to you can place a link on the desktop or somewhere else and trigger the link using a mapped key as follows;

F3::
;Open QGIS
path = "C:\Users\brooks.mark\Desktop\QGIS Desktop 2.14.8.lnk"
;MsgBox, %path%
 
Run, %path%
Return

Open Chrome using CTRL Z – (^ is the sign for CTRL)

^z::
Run, C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
Return

Here I have a key to open a work Uniform program – line send types into the username field usernamevariable – alter to your actual value

F6::
path = "C:\Users\brooks.mark\Desktop\Uniform LIVE.lnk"
;MsgBox, %path%
Run, %path%

Sleep, 5000

ControlFocus, Edit4,Uniform Spatial - LIVE Database
Send, usernamevariable

Return

Navigate to Website directly from MS Access using Internet Explorer

One of the major suppliers of planning software to the United Kingdom is a company called Idox Group plc. They produce probably the most popular back office software that runs all aspects of administering planning permission. As such their public access web pages usually have the same fundamental structure. What if rather than holding information about a planning application you would like to create a command button that would take the user directly to the planning application details.

Unfortunately the url bears no relation to the planning application reference so it is necessary to go to a search page enter the planning application number and then trigger search which if you have a completely accurate planning reference will take you to the individual record. Here’s a function for City of Edinburgh Council complete with relevant application number. Previously I had achieved this using AHK but here is an elegant solution using VBA code. As ever there are multiple ways to achieve the same thing.

Public Function GotoWebPage()

Dim ie As Object
Dim strapplication As String

strapplication = "18/00488/TCO"
Set ie = CreateObject("Internetexplorer.application")

ie.Visible = True

ie.navigate "https://citydev-portal.edinburgh.gov.uk/idoxpa-web/search.do?action=simple&searchType=Application"

While ie.busy
DoEvents
Wend

ie.Document.getElementbyID("simpleSearchString").Value = strapplication

ie.Document.Forms(0).submit

End Function

And here’s one for City of London Council

Public Function GotoWebPageCityLondon()

Dim ie As Object
Dim strapplication As String

strapplication = "18/00152/FULEIA"
Set ie = CreateObject("Internetexplorer.application")

ie.Visible = True

ie.navigate "http://www.planning2.cityoflondon.gov.uk/online-applications/search.do?action=simple&searchType=Application"

While ie.busy
DoEvents
Wend

ie.Document.getElementbyID("simpleSearchString").Value = strapplication

ie.Document.Forms(0).submit

End Function

Further useful reading
How to Navigate IE using VBA

Add Open Street Map to Background QGIS Project and then Digitise against imported Raster

The following is a workflow that can be used to get a raster base map of anything into QGIS which you then reference to Open Street Map Layers ready for digitising against. This will be useful for approximate digitising of masterplans and approximate digitisation of housing completions.

Firstly ensure you have dowloaded QGIS and added the following two plugins
OpenLayers Plugin

Georeferencer GDAL
Plugin

Opening QGIS now lets add the the Open Street Map Raster

From a blank project selection of Open Street Map should give you the following result

Now zoom to the approximate location where you wish to have a unique basemap. You will be referencing points on this map to points on your imported raster so you should zoom into a location to the extent that you can identify common locations between the two maps.

Identify the basemap you wish to have in your particular QGIS map here I choose freely available masterplan from Calderwood development in West Lothian from planning application 0524/P/09

Within the menus navigate to
Raster / Georeferencer /

You should be presented with the following window.

Hit the add raster button in the top left

Select the basemap you wish to add to your project and ensure that the coordinate system that you choose is OSGB 1936 / British National Grid

Next you want to add reference points to the basemap that will allow for you to put the basemap against it – This is done using the button marked

Next hit the settings button

You should now be presented with the Transformation parameters windows dialog as follows.
The dialog will remember old parameters if not ensure that you have the same selections (with your own selection of output raster location) as mine.

Now hit the play button the raster will be added to your map and the georeferencer will be reduced and moved to the bottom left of the corner where you will be open it and reduce it in size if you wish. You can now go in and alter the transparency so that it is possible to see both Open Street Map and your newly added raster

You should now be presented with something like the following – if there are red dots on the screen this is because you have not closed georeferencer down – simply open the window up again and hit file close.