Skip to content

Geospatial service that allows you to create locations and find nearby locations within a specified radius using PostGIS.

Notifications You must be signed in to change notification settings

Nabhag8848/location-service

Repository files navigation

Location Service

A NestJS-based geospatial service that allows you to create locations and find nearby locations within a specified radius using PostGIS.

Features

  • 📍 Create Locations: Store locations with names and coordinates
  • 🔍 Proximity Search: Find all locations within a specified radius from a given point
  • 🗺️ PostGIS Integration: Leverages PostgreSQL's PostGIS extension for efficient geospatial queries
  • âś… Validation: Built-in coordinate validation and data transformation

Prerequisites

  • Node.js (v14 or higher)
  • PostgreSQL with PostGIS extension enabled
  • NestJS application setup

Installation

1. Enable PostGIS Extension

Enable PostGIS extension in your PostgreSQL database:

CREATE EXTENSION IF NOT EXISTS postgis;

2. Install Dependencies

npm install @nestjs/common @nestjs/typeorm typeorm pg class-validator class-transformer

3. Import LocationModule

Import the LocationModule in your app module:

import { LocationModule } from './location/location.module';

@Module({
  imports: [
    // ... other imports
    LocationModule,
  ],
})
export class AppModule {}

Project Structure

location/
├── location.controller.ts    # HTTP endpoints
├── location.service.ts        # Business logic
├── location.entity.ts         # Database entity
├── location.dto.ts            # Data transfer objects
└── location.module.ts         # Module definition

API Endpoints

Create Location

Create a new location with name and coordinates.

Endpoint: POST /location

Request Body:

{
  "name": "Central Park",
  "latitude": 40.785091,
  "longitude": -73.968285
}

Response:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Central Park",
  "coordinates": {
    "type": "Point",
    "coordinates": [40.785091, -73.968285]
  }
}

Validation Rules:

  • name: Must be a string
  • latitude: Number between -90 and 90
  • longitude: Number between -180 and 180

Example cURL:

curl -X POST http://localhost:3000/location \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Central Park",
    "latitude": 40.785091,
    "longitude": -73.968285
  }'

Find Locations Within Range

Find all locations within a specified radius from a given point.

Endpoint: GET /location/radius

Query Parameters:

  • lat (required): Latitude of the center point
  • lon (required): Longitude of the center point
  • range (required): Search radius in kilometers

Example Request:

GET /location/radius?lat=40.785091&lon=-73.968285&range=5

Response:

[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Central Park",
    "coordinates": {
      "type": "Point",
      "coordinates": [40.785091, -73.968285]
    }
  },
  {
    "id": "660e8400-e29b-41d4-a716-446655440001",
    "name": "Times Square",
    "coordinates": {
      "type": "Point",
      "coordinates": [40.758896, -73.98513]
    }
  }
]

Example cURL:

curl -X GET "http://localhost:3000/location/radius?lat=40.785091&lon=-73.968285&range=5"

Database Schema

The service uses a single location table with the following structure:

Column Type Description
id UUID Primary key, auto-generated
name VARCHAR Location name
coordinates GEOGRAPHY(Point, 4326) Geospatial point using WGS 84 coordinate system

Entity Definition

@Entity()
export class Location {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column()
  name: string;

  @Column({
    type: 'geography',
    srid: 4326,
    spatialFeatureType: 'Point',
  })
  coordinates: Point;
}

How It Works

Coordinate Storage

Coordinates are stored as PostGIS GEOGRAPHY points with SRID 4326 (WGS 84), which is the standard coordinate reference system used by GPS.

Note: In the Point structure, coordinates are stored as [latitude, longitude].

Proximity Search

The service uses PostGIS's ST_DWithin function to efficiently find locations within a specified distance:

ST_DWithin(
  location.coordinates,
  ST_SetSRID(ST_MakePoint(:lon, :lat), 4326),
  :range
)

Features:

  • Takes the search radius in meters (automatically converted from kilometers)
  • Uses spatial indexing for fast queries
  • Returns accurate results accounting for Earth's curvature
  • Query format: ST_MakePoint(longitude, latitude) - note the order!

Error Handling

The service includes validation for:

Invalid Coordinates

Returns 400 Bad Request with message:

{
  "statusCode": 400,
  "message": "Invalid coordinates"
}

Invalid Input Data

Returns 400 Bad Request with validation errors:

{
  "statusCode": 400,
  "message": [
    "latitude must not be greater than 90",
    "longitude must not be less than -180"
  ],
  "error": "Bad Request"
}

Example Usage

Creating Multiple Locations

# Create location 1
curl -X POST http://localhost:3000/location \
  -H "Content-Type: application/json" \
  -d '{"name": "Statue of Liberty", "latitude": 40.689247, "longitude": -74.044502}'

# Create location 2
curl -X POST http://localhost:3000/location \
  -H "Content-Type: application/json" \
  -d '{"name": "Empire State Building", "latitude": 40.748817, "longitude": -73.985428}'

# Create location 3
curl -X POST http://localhost:3000/location \
  -H "Content-Type: application/json" \
  -d '{"name": "Brooklyn Bridge", "latitude": 40.706086, "longitude": -73.996864}'

Finding Nearby Locations

# Find all locations within 10km of Times Square
curl -X GET "http://localhost:3000/location/radius?lat=40.758896&lon=-73.985130&range=10"

Configuration

TypeORM Configuration

Ensure your TypeORM configuration includes:

TypeOrmModule.forRoot({
  type: 'postgres',
  host: 'localhost',
  port: 5432,
  username: 'your_username',
  password: 'your_password',
  database: 'your_database',
  entities: [Location],
  synchronize: true, // Set to false in production
});

Environment Variables

Consider using environment variables for database configuration:

DB_HOST=localhost
DB_PORT=5432
DB_USERNAME=your_username
DB_PASSWORD=your_password
DB_DATABASE=your_database

Performance Considerations

Spatial Index

For optimal performance with large datasets, create a spatial index:

CREATE INDEX location_coordinates_idx
ON location
USING GIST (coordinates);

TypeORM will automatically create this index when using the geography type.

Query Optimization

  • The ST_DWithin function uses the spatial index automatically
  • For very large datasets, consider partitioning by geographic regions
  • Use appropriate range values to limit result sets

Testing

Unit Tests

npm run test

E2E Tests

npm run test:e2e

Common Issues

PostGIS Extension Not Found

Error: type "geography" does not exist

Solution: Install and enable PostGIS:

CREATE EXTENSION IF NOT EXISTS postgis;

Coordinate Order Confusion

Important: Note the different coordinate orders:

  • DTO Input: latitude, longitude
  • Point Storage: [latitude, longitude]
  • PostGIS Functions: ST_MakePoint(longitude, latitude)

Range Units

The range parameter is in kilometers, which is automatically converted to meters for PostGIS queries.

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues and questions, please open an issue on the GitHub repository.

About

Geospatial service that allows you to create locations and find nearby locations within a specified radius using PostGIS.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published