Skip to content

Commit

Permalink
Merge pull request #204 from NickPhura/ACRFD-13
Browse files Browse the repository at this point in the history
ACRFD-13: Update spatial package + logic to support GeometryCollection.
  • Loading branch information
NickPhura authored May 8, 2019
2 parents fed9164 + 84db30b commit e44b53d
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 66 deletions.
43 changes: 40 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
# See https://git-scm.com/docs/gitignore for more about ignoring files.

# compiled output
/dist
/tmp
/out-tsc

# dependencies
node_modules
.DS_Store
.idea
uploads

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/launch.json

# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
package-lock.json
yarn-error.log
yarn.lock
debug.log

# Project Files
uploads

# System Files
.DS_Store
Thumbs.db
88 changes: 47 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,6 @@ Linting and formatting is handled by a combiation of `TSlint` and `Prettier`. T

These 2 linters (tslint, Prettier) do have overlapping rules. To avoid weird rule interactions, TSlint has been configured to defer any overlapping rules to Prettier, via the use of `tslint-config-prettier` in `tslint.json`.

Recommend installing the [VSCode Prettier extension](https://github.com/prettier/prettier-vscode), so Prettier's formatting can be applied on-the-fly.

### Technolgies used

[TSLint](https://palantir.github.io/tslint/), [Prettier](https://prettier.io/), [Stylelint](https://stylelint.io/), [husky](https://www.npmjs.com/package/husky), [lint-staged](https://github.com/okonet/lint-staged)
Expand Down Expand Up @@ -87,14 +85,6 @@ _Note: Not all linting/formatting errors can be automatically fixed, and will re
npm run lint-fix
```

# Testing

## Info

### Technolgies used

[Jasmine](https://jasmine.github.io/), [Karma](https://karma-runner.github.io/latest/index.html), [Protractor](http://www.protractortest.org/)

# API Specification

The API is defined in `swagger.yaml`.
Expand All @@ -105,7 +95,15 @@ This project uses npm package `swagger-tools` via `./app.js` to automatically ge

Recommend reviewing the [Open API Specification](https://swagger.io/docs/specification/about/) before making any changes to the `swagger.yaml` file.

# Initial Setup
# Testing

## Info

### Technolgies used

[Jasmine](https://jasmine.github.io/), [Karma](https://karma-runner.github.io/latest/index.html), [Protractor](http://www.protractortest.org/)

## Initial Setup

1) Start server and create database by running `npm start` in root

Expand All @@ -117,7 +115,7 @@ Recommend reviewing the [Open API Specification](https://swagger.io/docs/specifi

3) Seed local database as described in [seed README](seed/README.md)

# Testing
## API Testing

This project is using [jest](http://jestjs.io/) as a testing framework. You can run tests with
`yarn test` or `jest`. Running either command with the `--watch` flag will re-run the tests every time a file is changed.
Expand Down Expand Up @@ -170,12 +168,12 @@ This code will stand in for the swagger-tools router, and help build the objects
Unfortunately, this results in a lot of boilerplate code in each of the controller tests. There are some helpers to reduce the amount you need to write, but you will still need to check the parameter field names sent by your middleware router match what the controller(and swagger router) expect. However, this method results in pretty effective integration tests as they exercise the controller code and save objects in the database.


## Test Database
### Test Database
The tests run on an in-memory MongoDB server, using the [mongodb-memory-server](https://github.com/nodkz/mongodb-memory-server) package. The setup can be viewed at [test_helper.js](api/test/test_helper.js), and additional config in [config/mongoose_options.js]. It is currently configured to wipe out the database after each test run to prevent database pollution.

[Factory-Girl](https://github.com/aexmachina/factory-girl) is used to easily create models(persisted to db) for testing purposes.

## Mocking http requests
### Mocking http requests
External http calls (such as GETs to BCGW) are mocked with a tool called [nock](https://github.com/nock/nock). Currently sample JSON responses are stored in the [test/fixtures](test/fixtures) directory. This allows you to intercept a call to an external service such as bcgw, and respond with your own sample data.

```javascript
Expand All @@ -201,32 +199,7 @@ External http calls (such as GETs to BCGW) are mocked with a tool called [nock](
});
});
```

























## Running locally with Keycloak
### Running locally with Keycloak

This project uses [Keycloak](https://www.keycloak.org/) to handle authentication and manage user roles.

Expand All @@ -245,4 +218,37 @@ case 'http://localhost:4200':
// Local
this.keycloakEnabled = true;
break;
```
```

# VSCode Extensions

A list of recommended/helpful VS Code extensions.

## Linting/Formatting

* TSLint
* ESLint
* Prettier - Code formatter
* stylelint
* EditorConfig for VS Code

## Languages

* npm
* Angular Extension pack
* This may include 'Beautify' which should be disabled as we are using Prettier.
* JavaScript (ES6) code snippets

## General

* Auto Comment Blocks
* Auto-Open Markdown Preview
* autoDocstring
* Document This
* Better Comments
* Bracket Pair Colorizer
* Code Spell Checker
* Declarative Jenkinsfile Support
* Path intellisense
* SCSS intellisense
* Shell launcher
90 changes: 73 additions & 17 deletions api/helpers/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ var qs = require('qs');
var request = require('request');
var turf = require('@turf/turf');
var helpers = require('@turf/helpers');
var Wkt = require('wicket');
var Wkx = require('wkx');
const epsg = require('epsg');
const reproject = require('reproject');
const defaultLog = require('winston').loggers.get('default');
var _serviceHost = process.env.CLAMAV_SERVICE_HOST || '127.0.0.1';
var _servicePort = process.env.CLAMAV_SERVICE_PORT || '3310';
Expand Down Expand Up @@ -259,6 +261,13 @@ exports.getApplicationByFilenumber = function(accessToken, clFile) {
});
};

/**
* Fetches an application by its disposition ID.
*
* @param {string} accessToken tantalis bearer token
* @param {string} disp disposition ID
* @returns {Promise} promise that resolves with a single application
*/
exports.getApplicationByDispositionID = function(accessToken, disp) {
return new Promise(function(resolve, reject) {
defaultLog.info('Looking up disposition:', _tantalisAPI + 'landUseApplications/' + disp);
Expand Down Expand Up @@ -302,18 +311,7 @@ exports.getApplicationByDispositionID = function(accessToken, disp) {

// WKT conversion to GEOJSON
for (let geo of obj.interestParcels) {
var repro = null;
if (geo.wktGeometry) {
// convert to geojson
var wkt = new Wkt.Wkt();
wkt.read(geo.wktGeometry);
var geometry = wkt.toJson();

var epsg = require('epsg');
var reproject = require('reproject');

// Convert for use in leaflet coords.
repro = reproject.toWgs84(geometry, 'EPSG:3005', epsg);
var feature = {};
feature.TENURE_LEGAL_DESCRIPTION = geo.legalDescription;
feature.TENURE_AREA_IN_HECTARES = geo.areaInHectares;
Expand All @@ -327,11 +325,15 @@ exports.getApplicationByDispositionID = function(accessToken, disp) {
crs.properties = {};
crs.properties.name = 'urn:ogc:def:crs:EPSG::4326';

application.parcels.push({
type: 'Feature',
geometry: repro,
properties: feature,
crs: crs
const geometryArray = convertToLeafletFormat(convertToGeoJson(geo));

geometryArray.forEach(geometry => {
application.parcels.push({
type: 'Feature',
geometry: geometry,
properties: feature,
crs: crs
});
});
}
}
Expand Down Expand Up @@ -386,6 +388,60 @@ exports.getApplicationByDispositionID = function(accessToken, disp) {
});
};

/**
* Converts interestParcels wkt geometry data to an array of GeoJson format polygons.
*
* Note on type GeometryCollection: A GeometryCollection is the type when specifying a super-set of distinct polygons.
* But this application currently only supports polygons and stores them as separate Features (see models/features.js).
* So this function parses out the GeometryCollection sub-polygons rather than returning a single GeometryCollection
* object that has multiple sub-polygon elements.
*
* @param {*} geo element of interestParcels
* @returns [{coordinates: [], type: string}] array of geoJSON objects of the form: {coordinates: [], type: string}
*/
const convertToGeoJson = function(geo) {
if (!geo || !geo.wktGeometry) {
return [];
}

const geoJSONArray = [];

// convert to geojson
const geoJSON = Wkx.Geometry.parse(geo.wktGeometry).toGeoJSON();

if (geoJSON.type === 'GeometryCollection') {
// parse out GeometryCollection sub-polygons.
geoJSON.geometries.forEach(element => {
geoJSONArray.push(element);
});
} else {
geoJSONArray.push(geoJSON);
}

return geoJSONArray;
};

/**
* Converts an array of GeoJson format objects to an array of EPSG:3005 format objects usable by leaflet.
*
* @param [{coordinates: [], type: string}] array of geoJson format objects of the form: {coordinates: [], type: string}
* @returns [{coordinates: [], type: string}] array of EPSG:3005 format objects of the form: {coordinates: [], type: string}
*/
const convertToLeafletFormat = function(geoJSONArray) {
if (!geoJSONArray) {
return [];
}

const leafletFormatArray = [];

geoJSONArray.forEach(element => {
// Convert for use in leaflet coords.
leafletFormatArray.push(reproject.toWgs84(element, 'EPSG:3005', epsg));
});

return leafletFormatArray;
};

/**
* Fetches all application landUseApplicationIds (aka: dispositionID, tantalisID) from Tantalis given the filter params provided.
*
Expand Down
11 changes: 9 additions & 2 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ var YAML = require('yamljs');
var swaggerConfig = YAML.load('./api/swagger/swagger.yaml');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
const moment = require('moment');

// winston logger needs to be created before any local classes (that use the logger) are loaded.
// winston logger needs to be created before any local classes that use the logger are loaded.
const winston = require('winston');
const defaultLog = winston.loggers.add('default', {
transports: [new winston.transports.Console({ level: 'silly', handleExceptions: true })]
transports: [
new winston.transports.Console({
formatter: info => {
return `${moment().format('DD-MM-YYYY HH:mm:ss')} ${info.level}: ${info.message}`;
}
})
]
});

var auth = require('./api/helpers/auth');
Expand Down
7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"scripts": {
"start": "node app",
"test": "echo Test suite should be run with `npm run tests` or npm `run tests-ci`",
"tests": "UPLOAD_DIRECTORY='./api/test/uploads/' jest api --runInBand",
"tests": "SET UPLOAD_DIRECTORY='./api/test/uploads/' & jest api --runInBand",
"tests-debug": "node --inspect node_modules/.bin/jest --runInBand",
"coverage": "jest --collectCoverageFrom=api/**.js --coverage api",
"lint": "eslint .",
Expand All @@ -34,6 +34,7 @@
"flake-idgen": "1.1.2",
"jsonwebtoken": "8.5.0",
"jwks-rsa": "1.4.0",
"moment": "2.24.0",
"mongoose": "5.4.17",
"nconf": "0.10.0",
"passport": "0.4.0",
Expand All @@ -46,8 +47,8 @@
"swagger-tools": "0.10.4",
"underscore": "1.9.1",
"validator": "10.11.0",
"wicket": "1.3.3",
"winston": "3.2.1",
"wkx": "0.4.6",
"winston": "2.4.4",
"yamljs": "0.3.0"
},
"devDependencies": {
Expand Down

0 comments on commit e44b53d

Please sign in to comment.