Skip to content

Commit

Permalink
🚧 integrating postgis & geojson
Browse files Browse the repository at this point in the history
  • Loading branch information
MattMoony committed Jun 2, 2024
1 parent 68d8c48 commit 948932c
Show file tree
Hide file tree
Showing 9 changed files with 60 additions and 19 deletions.
7 changes: 7 additions & 0 deletions core/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/express-fileupload": "^1.4.4",
"@types/geojson": "^7946.0.14",
"@types/jsonwebtoken": "^9.0.6",
"@types/lru-cache": "^7.10.10",
"@types/node": "^20.11.10",
Expand Down
4 changes: 4 additions & 0 deletions core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import personRouter from './routes/person';
import organizationRouter from './routes/organization';
import roleRouter from './routes/role';
import authRouter from './routes/auth';
import locationRouter from './routes/location';
import nationRouter from './routes/nation';

dotenv.config();

Expand All @@ -38,6 +40,8 @@ app.use('/api/organ/', organRouter);
app.use('/api/person/', personRouter);
app.use('/api/organization/', organizationRouter);
app.use('/api/role/', roleRouter);
app.use('/api/location/', locationRouter);
app.use('/api/nation/', nationRouter);
app.use('/api/auth/', authRouter);

app.get('/api/stats', async (req: Request, res: Response) => {
Expand Down
2 changes: 1 addition & 1 deletion core/src/middleware/nation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export const nid2nation = async (req: Request, res: Response, next: () => void):
return;
}
const nation = await Nation.get(parseInt(req.params.nid));
if (location === null) {
if (nation === null) {
res.send({ 'success': false, 'msg': 'nation not found' });
return;
}
Expand Down
22 changes: 10 additions & 12 deletions core/src/models/Location.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ const log = baseLogger('location');
class Location {
public id: number;
public name: string;
public coords?: [number, number]|null;
public coords?: GeoJSON.Point|null;

public constructor (id: number, name: string, coords?: [number, number]|null) {
public constructor (id: number, name: string, coords?: GeoJSON.Point|null) {
this.id = id;
this.name = name;
if (coords && coords.length !== 2) throw new Error('Location coordinates must be a tuple of two numbers.');
this.coords = coords;
this.cache();
}
Expand All @@ -43,8 +42,7 @@ class Location {
return {
id: this.id,
name: this.name,
lat: this.coords?.at(0),
lng: this.coords?.at(1),
coords: this.coords,
};
}

Expand All @@ -61,7 +59,7 @@ class Location {
* @returns A promise that resolves when the location has been updated.
*/
public async update (): Promise<void> {
await pool.query('UPDATE location SET name = $1, coords = $2 WHERE lid = $3', [this.name, this.coords, this.id]);
await pool.query('UPDATE location SET name = $1, coords = st_geomfromgeojson($2) WHERE lid = $3', [this.name, this.coords, this.id]);
log.debug(`Updated location ${this}`);
this.cache();
}
Expand All @@ -82,8 +80,8 @@ class Location {
* @param coords The coordinates of the location.
* @returns A promise that resolves with the new location.
*/
public static async create (name: string, coords?: [number, number]): Promise<Location> {
const { rows } = await pool.query('INSERT INTO locations (name, coords) VALUES ($1, $2) RETURNING lid', [name, coords]);
public static async create (name: string, coords?: GeoJSON.Point): Promise<Location> {
const { rows } = await pool.query('INSERT INTO locations (name, coords) VALUES ($1, st_geomfromgeojson($2)) RETURNING lid', [name, coords]);
return new Location(+rows[0].lid, name, coords);
}

Expand All @@ -96,10 +94,10 @@ class Location {
id = +id;
const cached = LOCATION_CACHE.get(id);
if (cached === undefined || !(cached instanceof Location)) {
const { rows } = await pool.query('SELECT * FROM location WHERE lid = $1', [id,]);
const { rows } = await pool.query('SELECT lid, name, st_asgeojson(coords) "coords" FROM location WHERE lid = $1', [id,]);
if (rows.length === 0) return null;
const { name, coords } = rows[0];
return new Location(+id, name, coords);
return new Location(+id, name, JSON.parse(coords));
}
log.debug(`Hit location cache ${id}`);
return (cached as Location) || null;
Expand All @@ -111,8 +109,8 @@ class Location {
* @returns A promise that resolves with the locations found.
*/
public static async find (query: string): Promise<Location[]> {
const { rows } = await pool.query('SELECT * FROM location WHERE name ILIKE $1', [`%${query}%`]);
return rows.map(({ lid, name, coords }) => new Location(+lid, name, coords));
const { rows } = await pool.query('SELECT lid, name, st_asgeojson(coords) "coords" FROM location WHERE name ILIKE $1', [`%${query}%`]);
return rows.map(({ lid, name, coords }) => new Location(+lid, name, JSON.parse(coords)));
}
}

Expand Down
14 changes: 9 additions & 5 deletions core/src/models/Nation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ export interface NationCache extends OrganizationCache {}
* Represents a political nation.
*/
class Nation extends Organization {
public geo?: GeoJSON.Polygon|null;

protected _cache: NationCache = {};

public constructor (id: number, bio: string, name: string, established?: Date|null, dissolved?: Date|null, location?: Location|null) {
public constructor (id: number, bio: string, name: string, established?: Date|null, dissolved?: Date|null, location?: Location|null, geo?: GeoJSON.Polygon|null) {
super(id, bio, name, established, dissolved, location);
this.geo = geo;
this.cache();
}

Expand All @@ -51,7 +54,7 @@ class Nation extends Organization {
public json (): Object {
return {
...super.json(),
location: this.location?.json(),
geo: this.geo,
};
}

Expand Down Expand Up @@ -110,19 +113,20 @@ class Nation extends Organization {
if (cached === undefined || !(cached instanceof Nation)) {
const client = await pool.connect();
const result = await client.query(
'SELECT * FROM nation WHERE nid = $1',
'SELECT st_asgeojson(geo) "geo" FROM nation WHERE nid = $1',
[id]
);
client.release();
if (result.rowCount === 0) return null;
const location = result.rows[0].location;
const geo = result.rows[0].geo;
return new Nation(
id,
organization.bio,
organization.name,
organization.established,
organization.dissolved,
location ? await Location.get(location) : undefined
organization.location,
geo ? JSON.parse(geo) : null
);
}
log.debug(`Hit nation cache ${id}`);
Expand Down
3 changes: 2 additions & 1 deletion db/define.sql
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ CREATE TABLE tag (
CREATE TABLE location (
lid BIGSERIAL,
name VARCHAR(256) NOT NULL,
coords POINT,
coords GEOGRAPHY(POINT),

PRIMARY KEY (lid)
);
Expand All @@ -73,6 +73,7 @@ CREATE TABLE organization (

CREATE TABLE nation (
nid BIGINT REFERENCES organization(oid),
geo GEOGRAPHY(POLYGON),

PRIMARY KEY (nid)
);
Expand Down
24 changes: 24 additions & 0 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
"@fortawesome/free-regular-svg-icons": "^6.5.1",
"@fortawesome/free-solid-svg-icons": "^6.5.1",
"@fortawesome/vue-fontawesome": "^3.0.5",
"@vue-leaflet/vue-leaflet": "^0.10.1",
"@vue/apollo-composable": "^4.0.1",
"@vueuse/core": "^10.7.2",
"animejs": "^3.2.2",
Expand All @@ -37,6 +38,7 @@
"dompurify": "^3.1.2",
"graphql": "^16.8.1",
"graphql-tag": "^2.12.6",
"leaflet": "^1.9.4",
"marked": "^12.0.2",
"marked-alert": "^2.0.1",
"marked-linkify-it": "^3.1.9",
Expand Down

0 comments on commit 948932c

Please sign in to comment.