Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add geolocations #173

Merged
merged 5 commits into from
Feb 19, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 36 additions & 1 deletion app/models/index.js
Expand Up @@ -9,6 +9,7 @@ const csv = require("csv");
const env = process.env.NODE_ENV || "development";
const defaults = require(path.join(__dirname, "../../config/config.js"))(env);
const config = defaults.postgres;
const OSPoint = require("ospoint");

// Instantiate postgres client pool
const pool = pg.Pool(config);
Expand Down Expand Up @@ -168,6 +169,40 @@ Base.prototype._getClient = function (callback) {

const dollarise = values => values.map((_, i) => `$${i + 1}`).join(", ");

function populateLocation(callback) {
/* jshint validthis: true */
const query = `
UPDATE
${this.relation}
SET
location=ST_GeogFromText(
'SRID=4326;POINT(' || longitude || ' ' || latitude || ')'
)
WHERE
northings!=0
AND EASTINGS!=0
`;
this._query(query, callback);
}

function getLocation(options) {
const { northings, eastings, country } = options;
let location;
if (eastings.length === 0 || northings.length === 0) {
location = {
longitude: "",
latitude: ""
};
} else if (country === "N92000002") { // Is Irish grid reference
location = new OSPoint("" + northings , "" + eastings).toWGS84("irish_national_grid");
} else {
location = new OSPoint("" + northings , "" + eastings).toWGS84();
}
return location;
}

module.exports = {
Base: Base
Base: Base,
populateLocation: populateLocation,
getLocation: getLocation
};
44 changes: 14 additions & 30 deletions app/models/postcode.js
Expand Up @@ -4,8 +4,7 @@ const util = require("util");
const path = require("path");
const Pc = require("postcode");
const async = require("async");
const OSPoint = require("ospoint");
const Base = require("./index").Base;
const { Base, populateLocation, getLocation } = require("./index");
const QueryStream = require("pg-query-stream");
const env = process.env.NODE_ENV || "development";
const configPath = path.join(__dirname, "../../config/config.js");
Expand Down Expand Up @@ -737,20 +736,18 @@ Postcode.prototype.seedPostcodes = function (filePath, callback) {

finalRow.push(row[2]); // postcode
finalRow.push(row[2].replace(/\s/g, "")); // pc_compact
finalRow.push(row[9]); // Eastings
finalRow.push(row[10]); // Northings

let location;
if (row[9].length === 0 || row[10].length === 0) {
location = {
longitude: "",
latitude: ""
};
} else if (row[14] === "N92000002") {
location = new OSPoint("" + row[10] , "" + row[9]).toWGS84("irish_national_grid");
} else {
location = new OSPoint("" + row[10] , "" + row[9]).toWGS84();
}

const eastings = row[9];
finalRow.push(eastings); // Eastings
const northings = row[10];
finalRow.push(northings); // Northings

const country = row[14];
const location = getLocation({
eastings: eastings,
northings: northings,
country: country
});

finalRow.push(location.longitude); // longitude
finalRow.push(location.latitude); // latitude
Expand Down Expand Up @@ -782,19 +779,6 @@ Postcode.prototype.seedPostcodes = function (filePath, callback) {
}, callback);
};

Postcode.prototype.populateLocation = function (callback) {
const query = `
UPDATE
${this.relation}
SET
location=ST_GeogFromText(
'SRID=4326;POINT(' || longitude || ' ' || latitude || ')'
)
WHERE
northings!=0
AND EASTINGS!=0
`;
this._query(query, callback);
};
Postcode.prototype.populateLocation = populateLocation;

module.exports = new Postcode();
36 changes: 32 additions & 4 deletions app/models/terminated_postcode.js
@@ -1,7 +1,7 @@
"use strict";

const util = require("util");
const Base = require("./index").Base;
const { Base, populateLocation, getLocation } = require("./index");
const async = require("async");
const Pc = require("postcode");

Expand All @@ -10,7 +10,12 @@ const TerminatedPostcodeSchema = {
"postcode": `VARCHAR(10) NOT NULL COLLATE "C"`,
"pc_compact" : `VARCHAR(9) COLLATE "C"`,
"year_terminated" : "INTEGER",
"month_terminated": "INTEGER"
"month_terminated": "INTEGER",
"eastings" : "INTEGER",
"northings" : "INTEGER",
"longitude" : "DOUBLE PRECISION",
"latitude" : "DOUBLE PRECISION",
"location" : "GEOGRAPHY(Point, 4326)"
};

const indexes = [{
Expand Down Expand Up @@ -44,7 +49,7 @@ TerminatedPostcode.prototype.find = function (postcode, callback) {
};

TerminatedPostcode.prototype.whitelistedAttributes = [
"postcode", "year_terminated", "month_terminated"
"postcode", "year_terminated", "month_terminated", "longitude", "latitude"
];

/**
Expand All @@ -64,7 +69,11 @@ TerminatedPostcode.prototype.seedPostcodes = function (filePath,callback) {
"postcode",
"pc_compact",
"year_terminated",
"month_terminated"
"month_terminated",
"eastings",
"northings",
"longitude",
"latitude"
];

const transform = row => {
Expand All @@ -77,6 +86,22 @@ TerminatedPostcode.prototype.seedPostcodes = function (filePath,callback) {
finalRow.push(row[2].replace(/\s/g, "")); // pc_compact
finalRow.push(parseInt(row[4].slice(0,4), 10)); //year_terminated
finalRow.push(parseInt(row[4].slice(-2), 10)); //month_terminated

const eastings = row[9];
finalRow.push(eastings); // Eastings
const northings = row[10];
finalRow.push(northings); // Northings

const country = row[14];
const location = getLocation({
eastings: eastings,
northings: northings,
country: country
});

finalRow.push(location.longitude); // longitude
finalRow.push(location.latitude); // latitude

return finalRow;
};

Expand All @@ -95,8 +120,11 @@ TerminatedPostcode.prototype._setupTable = function (filePath, callback) {
function seedData (cb) {
self.seedPostcodes(filePath, cb);
},
self.populateLocation.bind(self),
self.createIndexes.bind(self),
], callback);
};

TerminatedPostcode.prototype.populateLocation = populateLocation;

module.exports = new TerminatedPostcode();
27 changes: 18 additions & 9 deletions bin/import.js
Expand Up @@ -28,9 +28,12 @@ if (!sourceFile) {
throw new Error("Aborting Import. No source file specified");
}

function dropRelation (callback) {
console.log("Nuking old postcode database...");
Postcode._destroyRelation(callback);
function dropRelations (callback) {
console.log("Dropping Postcode and Terminated Postcode table...");
async.series([
Postcode._destroyRelation.bind(Postcode),
TerminatedPostcode._destroyRelation.bind(TerminatedPostcode),
], callback);
}

function createRelation (callback) {
Expand Down Expand Up @@ -83,7 +86,7 @@ function setupTerminatedPostcodes (callback) {
}

var executionStack = [createPostgisExtension,
dropRelation,
dropRelations,
setupSupportTables,
createRelation,
importRawCsv,
Expand All @@ -97,7 +100,7 @@ function startImport () {
if (error) {
console.log("Unable to complete import process due to error", error);
console.log("Dropping newly created relation")
dropRelation(function (error, result) {
dropRelations(function (error, result) {
if (error) {
console.log("Unabled to drop relation");
process.exit(1);
Expand All @@ -111,16 +114,22 @@ function startImport () {

prompt.start();

const message = `
Importing data will wipe your current postcode database before continuing
If you already have existing data please consider using updateons

Type 'YES' to continue
`;

prompt.get([{
description: "Importing data will wipe your current postcode database before continuing. If you already have existing data please consider using updateons. Type YES to continue",
name: 'userIsSure',
warning: 'Username must be only letters, spaces, or dashes'
message: message,
name: 'confirmation',
}], function (error, result) {
if (error) {
console.log(error);
process.exit(1);
}
if (result.userIsSure === "YES") {
if (result.confirmation === "YES") {
startImport();
} else {
console.log("You have opted to cancel the import process");
Expand Down
19 changes: 18 additions & 1 deletion tests/base.unit.js
Expand Up @@ -2,7 +2,9 @@

const helper = require("./helper");
const assert = require("chai").assert;

const path = require("path");
const rootPath = path.join(__dirname, "../");
const getLocation = require(path.join(rootPath, "app/models")).getLocation;
const Base = helper.Base;

describe("Base model", function () {
Expand Down Expand Up @@ -168,4 +170,19 @@ describe("Base model", function () {
});
});
});
});

describe("#getLocation", () => {
it ("returns empty locations if eastings/northings is empty string", () => {
const expectedLocation = {longitude: "", latitude: ""};
let location = getLocation({eastings: "", northings: "43"});
assert.deepEqual(location, expectedLocation);
location = getLocation({eastings: "43", northings: ""});
assert.deepEqual(location, expectedLocation);
});
it ("returns different coordinates, if specified as Ireland", () => {
let locationNotIreland = getLocation({eastings: "334316", northings: "374675"});
let locationIreland = getLocation({eastings: "334316", northings: "374675", country: "N92000002"});
assert.notDeepEqual(locationIreland, locationNotIreland);
});
});
20 changes: 19 additions & 1 deletion tests/terminated_postcodes.integration.js
Expand Up @@ -35,7 +35,7 @@ describe("Terminated postcode route", () => {
});

describe("/GET /terminated_postcodes/:postcode", () => {
it ("should return 200 and the correct result if terminated postcode found", done => {
it ("should return 200 and only whitelisted attributes if terminated postcode found", done => {
path = `/terminated_postcodes/${encodeURI(testTerminatedPostcode)}`;
request(app)
.get(path)
Expand All @@ -48,6 +48,24 @@ describe("Terminated postcode route", () => {
helper.isTerminatedPostcodeObject(response.body.result);
done();
});
});
it ("only returns postcode, month and year of termination, longitude and latitude", done => {
path = `/terminated_postcodes/${encodeURI(testTerminatedPostcode)}`;
request(app)
.get(path)
.expect("Content-Type", /json/)
.expect(200)
.end( (error, response) => {
if (error) return done(error);
const result = response.body.result;
assert.equal(Object.keys(result).length, 5);
assert.isDefined(result.postcode);
assert.isDefined(result.year_terminated);
assert.isDefined(result.month_terminated);
assert.isDefined(result.longitude);
assert.isDefined(result.latitude);
done();
});
});
it ("returns 200 with the correct data if terminated postcode has extra spaces", done => {
testTerminatedPostcode = " " + testTerminatedPostcode + " ";
Expand Down