Appendix B: Coordinate Systems and Projections#

This appendix provides a comprehensive reference for coordinate systems and map projections commonly used in Web GIS applications. Understanding coordinate systems is crucial for accurate spatial data handling, coordinate transformations, and map display.

Coordinate System Fundamentals#

Geographic Coordinate Systems (GCS)#

Geographic coordinate systems use angular units (degrees) to specify locations on the Earth’s surface using latitude and longitude coordinates.

Key Components:

  • Datum: Mathematical model of the Earth’s shape

  • Prime Meridian: Reference longitude (usually Greenwich at 0°)

  • Angular Unit: Typically decimal degrees

Common Geographic Coordinate Systems:

WGS84 (EPSG:4326)#

  • Full Name: World Geodetic System 1984

  • Usage: GPS, web mapping, international standards

  • Extent: Global

  • Prime Meridian: Greenwich

  • Ellipsoid: WGS 84

// WGS84 coordinate example
const wgs84Point = {
  latitude: 37.7749, // North is positive
  longitude: -122.4194, // West is negative
  srs: "EPSG:4326",
};

NAD83 (EPSG:4269)#

  • Full Name: North American Datum 1983

  • Usage: North American mapping and surveying

  • Extent: North America

  • Ellipsoid: GRS 1980

  • Accuracy: Higher precision than WGS84 for North America

NAD27 (EPSG:4267)#

  • Full Name: North American Datum 1927

  • Usage: Legacy North American data

  • Extent: North America

  • Ellipsoid: Clarke 1866

  • Note: Superseded by NAD83

Projected Coordinate Systems (PCS)#

Projected coordinate systems transform the curved Earth surface onto a flat plane using mathematical projections, with coordinates typically in linear units (meters, feet).

Key Components:

  • Projection Method: Mathematical transformation algorithm

  • Parameters: False easting, false northing, central meridian, etc.

  • Linear Unit: Meters, feet, etc.

  • Geographic Coordinate System: Underlying datum

Web Mercator Projection (EPSG:3857)#

Web Mercator is the most important projection for web mapping applications.

Characteristics#

  • Also Known As: Pseudo-Mercator, Spherical Mercator

  • EPSG Codes: 3857, 900913 (deprecated)

  • Units: Meters

  • Extent: -20037508.34 to 20037508.34 (x and y)

  • Latitude Range: Approximately ±85.05°

Mathematical Definition#

// Web Mercator projection formulas
class WebMercator {
  static readonly EARTH_RADIUS = 6378137; // WGS84 equatorial radius
  static readonly MAX_EXTENT = 20037508.342789244;

  // Convert lat/lon to Web Mercator coordinates
  static project([lng, lat]) {
    const x = lng * this.EARTH_RADIUS * Math.PI / 180;
    const y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) * this.EARTH_RADIUS;
    return [x, y];
  }

  // Convert Web Mercator coordinates to lat/lon
  static unproject([x, y]) {
    const lng = x / this.EARTH_RADIUS * 180 / Math.PI;
    const lat = (2 * Math.atan(Math.exp(y / this.EARTH_RADIUS)) - Math.PI / 2) * 180 / Math.PI;
    return [lng, lat];
  }

  // Check if coordinates are within valid Web Mercator bounds
  static isValid([x, y]) {
    return Math.abs(x) <= this.MAX_EXTENT && Math.abs(y) <= this.MAX_EXTENT;
  }
}

Usage in Web GIS#

Advantages:

  • Universal adoption by web mapping platforms

  • Simple mathematical properties

  • Fast calculation

  • Square pixels at all zoom levels

Limitations:

  • Significant distortion at high latitudes

  • Cannot display polar regions

  • Area distortion increases away from equator

Example Implementation:

// Tile coordinate calculation in Web Mercator
function getWebMercatorTile(lat, lng, zoom) {
  // Project to Web Mercator
  const [x, y] = WebMercator.project([lng, lat]);

  // Convert to tile coordinates
  const tileSize = 256;
  const resolution =
    (2 * WebMercator.MAX_EXTENT) / (tileSize * Math.pow(2, zoom));

  const tileX = Math.floor(
    (x + WebMercator.MAX_EXTENT) / (resolution * tileSize)
  );
  const tileY = Math.floor(
    (WebMercator.MAX_EXTENT - y) / (resolution * tileSize)
  );

  return { x: tileX, y: tileY, z: zoom };
}

State Plane Coordinate Systems#

State Plane Coordinate Systems (SPCS) provide high-accuracy coordinate systems for specific US states and regions.

SPCS 1983 (NAD83)#

Based on NAD83 datum, using either Lambert Conformal Conic or Transverse Mercator projections.

Common Examples:

California Zone 3 (EPSG:2227)#

  • Projection: Lambert Conformal Conic

  • Units: US Survey Feet

  • Central Meridian: -120.5°

  • Standard Parallels: 37.066667°, 38.433333°

// Example coordinate transformation to California State Plane
const californiaZone3 = {
  epsg: 2227,
  proj4:
    "+proj=lcc +lat_1=37.06666666666667 +lat_2=38.43333333333333 +lat_0=36.5 +lon_0=-120.5 +x_0=609601.2192024384 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs",
};

New York Long Island (EPSG:2263)#

  • Projection: Lambert Conformal Conic

  • Units: US Survey Feet

  • Central Meridian: -74°

  • Standard Parallels: 40.666667°, 41.033333°

SPCS 1927 (NAD27)#

Legacy system based on NAD27 datum, still used for some historical data.

UTM (Universal Transverse Mercator)#

UTM divides the Earth into 60 zones, each 6° wide, using Transverse Mercator projection.

Zone Characteristics#

  • Zone Width: 6° longitude

  • Zone Numbering: 1-60, starting at 180°W

  • Hemisphere: N (Northern) or S (Southern)

  • Central Scale Factor: 0.9996

  • False Easting: 500,000 meters

  • False Northing: 0 (North), 10,000,000 (South)

Zone Calculation#

function getUTMZone(longitude, latitude) {
  // Calculate zone number
  const zoneNumber = Math.floor((longitude + 180) / 6) + 1;
  const hemisphere = latitude >= 0 ? "N" : "S";

  return {
    zone: zoneNumber,
    hemisphere: hemisphere,
    epsg: latitude >= 0 ? 32600 + zoneNumber : 32700 + zoneNumber,
  };
}

// Example usage
const utmZone = getUTMZone(-122.4194, 37.7749); // Zone 10N
console.log(utmZone); // { zone: 10, hemisphere: 'N', epsg: 32610 }

UTM Zone Coverage#

Zone Examples:

  • Zone 10N (EPSG:32610): US West Coast

  • Zone 11N (EPSG:32611): US Southwest

  • Zone 17N (EPSG:32617): US East Coast

  • Zone 33N (EPSG:32633): Central Europe

Coordinate Transformations#

Using Proj4js#

Proj4js is the standard JavaScript library for coordinate transformations.

import proj4 from "proj4";

// Define coordinate systems
proj4.defs(
  "EPSG:2227",
  "+proj=lcc +lat_1=37.06666666666667 +lat_2=38.43333333333333 +lat_0=36.5 +lon_0=-120.5 +x_0=609601.2192024384 +y_0=0 +ellps=GRS80 +datum=NAD83 +to_meter=0.3048006096012192 +no_defs"
);

// Transform coordinates
const wgs84 = [-122.4194, 37.7749];
const statePlane = proj4("EPSG:4326", "EPSG:2227", wgs84);
console.log(statePlane); // [6005574.12, 2089471.84] (approximate)

// Create reusable transformer
const transformer = proj4("EPSG:4326", "EPSG:3857");
const webMercator = transformer.forward([-122.4194, 37.7749]);

Transformation Pipeline#

class CoordinateTransformer {
  constructor() {
    this.transformers = new Map();
    this.initializeCommonSystems();
  }

  initializeCommonSystems() {
    // Define common coordinate systems
    const definitions = {
      "EPSG:4326": "+proj=longlat +datum=WGS84 +no_defs",
      "EPSG:3857":
        "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs",
      "EPSG:32610": "+proj=utm +zone=10 +datum=WGS84 +units=m +no_defs",
    };

    Object.entries(definitions).forEach(([code, definition]) => {
      proj4.defs(code, definition);
    });
  }

  transform(coordinates, fromSRS, toSRS) {
    const key = `${fromSRS}-${toSRS}`;

    if (!this.transformers.has(key)) {
      this.transformers.set(key, proj4(fromSRS, toSRS));
    }

    const transformer = this.transformers.get(key);
    return transformer.forward(coordinates);
  }

  transformBounds(bounds, fromSRS, toSRS) {
    const [minX, minY, maxX, maxY] = bounds;

    // Transform corner points
    const bottomLeft = this.transform([minX, minY], fromSRS, toSRS);
    const topRight = this.transform([maxX, maxY], fromSRS, toSRS);
    const bottomRight = this.transform([maxX, minY], fromSRS, toSRS);
    const topLeft = this.transform([minX, maxY], fromSRS, toSRS);

    // Find new bounds
    const allX = [bottomLeft[0], topRight[0], bottomRight[0], topLeft[0]];
    const allY = [bottomLeft[1], topRight[1], bottomRight[1], topLeft[1]];

    return [
      Math.min(...allX),
      Math.min(...allY),
      Math.max(...allX),
      Math.max(...allY),
    ];
  }

  isValidTransformation(fromSRS, toSRS) {
    try {
      this.transform([0, 0], fromSRS, toSRS);
      return true;
    } catch (error) {
      return false;
    }
  }
}

Projection Properties and Selection#

Projection Categories#

Conformal Projections#

  • Property: Preserve angles and shapes locally

  • Examples: Mercator, Lambert Conformal Conic, Transverse Mercator

  • Use Cases: Navigation, topographic maps, web mapping

Equal-Area Projections#

  • Property: Preserve area relationships

  • Examples: Albers Equal Area, Lambert Azimuthal Equal Area

  • Use Cases: Statistical mapping, density analysis

Equidistant Projections#

  • Property: Preserve distances from specific points

  • Examples: Azimuthal Equidistant, Equidistant Conic

  • Use Cases: Distance analysis, radio transmission maps

Distortion Analysis#

class ProjectionAnalyzer {
  // Calculate scale factor at a point
  static getScaleFactor(projection, longitude, latitude) {
    const deltaLon = 0.001;
    const deltaLat = 0.001;

    // Project center point and nearby points
    const center = proj4(projection, [longitude, latitude]);
    const east = proj4(projection, [longitude + deltaLon, latitude]);
    const north = proj4(projection, [longitude, latitude + deltaLat]);

    // Calculate distances
    const distanceEast = Math.hypot(east[0] - center[0], east[1] - center[1]);
    const distanceNorth = Math.hypot(
      north[0] - center[0],
      north[1] - center[1]
    );

    // Calculate expected distances (on sphere)
    const earthRadius = 6378137;
    const expectedEast =
      ((earthRadius * deltaLon * Math.PI) / 180) *
      Math.cos((latitude * Math.PI) / 180);
    const expectedNorth = (earthRadius * deltaLat * Math.PI) / 180;

    return {
      scaleX: distanceEast / expectedEast,
      scaleY: distanceNorth / expectedNorth,
      avgScale:
        (distanceEast / expectedEast + distanceNorth / expectedNorth) / 2,
    };
  }

  // Analyze distortion across a region
  static analyzeDistortion(projection, bounds, gridSize = 10) {
    const [minX, minY, maxX, maxY] = bounds;
    const stepX = (maxX - minX) / gridSize;
    const stepY = (maxY - minY) / gridSize;

    const analysis = [];

    for (let i = 0; i <= gridSize; i++) {
      for (let j = 0; j <= gridSize; j++) {
        const lon = minX + i * stepX;
        const lat = minY + j * stepY;

        const scaleFactor = this.getScaleFactor(projection, lon, lat);
        analysis.push({
          longitude: lon,
          latitude: lat,
          ...scaleFactor,
        });
      }
    }

    return analysis;
  }
}

Common Web GIS Coordinate Systems#

Global Systems#

EPSG Code

Name

Type

Usage

4326

WGS84

Geographic

GPS, web services, global data

3857

Web Mercator

Projected

Web mapping, tile services

4269

NAD83

Geographic

North American data

3395

World Mercator

Projected

Global mapping (true Mercator)

Regional Systems (United States)#

EPSG Code

Name

Coverage

Units

2227

CA State Plane Zone 3

Central California

US Survey Feet

2263

NY State Plane Long Island

New York

US Survey Feet

2248

MD State Plane

Maryland

Meters

32610

UTM Zone 10N

US West Coast

Meters

32617

UTM Zone 17N

US East Coast

Meters

European Systems#

EPSG Code

Name

Coverage

Usage

4258

ETRS89

Europe

Pan-European datum

3035

ETRS89 LAEA

Europe

Statistical analysis

25832

ETRS89 UTM 32N

Central Europe

Mapping

27700

British National Grid

United Kingdom

UK mapping

Coordinate System Detection#

class CoordinateSystemDetector {
  static detectFromBounds(bounds) {
    const [minX, minY, maxX, maxY] = bounds;

    // Check Web Mercator bounds
    if (
      Math.abs(minX) <= 20037508.34 &&
      Math.abs(maxX) <= 20037508.34 &&
      Math.abs(minY) <= 20037508.34 &&
      Math.abs(maxY) <= 20037508.34 &&
      (Math.abs(minX) > 180 || Math.abs(maxX) > 180)
    ) {
      return "EPSG:3857";
    }

    // Check WGS84 bounds
    if (minX >= -180 && maxX <= 180 && minY >= -90 && maxY <= 90) {
      return "EPSG:4326";
    }

    // Check UTM bounds (rough estimate)
    if (
      minX > 100000 &&
      maxX < 900000 &&
      ((minY > 0 && maxY < 10000000) || (minY > 9000000 && maxY < 20000000))
    ) {
      return "UTM"; // Further analysis needed for specific zone
    }

    // Check State Plane bounds (US Survey Feet)
    if (minX > 1000000 && maxX < 10000000 && minY > 0 && maxY < 10000000) {
      return "STATE_PLANE_FEET";
    }

    return "UNKNOWN";
  }

  static detectUTMZone(bounds) {
    // Convert bounds to geographic coordinates first
    const [minX, minY, maxX, maxY] = bounds;
    const centerX = (minX + maxX) / 2;
    const centerY = (minY + maxY) / 2;

    // Estimate zone from center coordinates
    if (centerX > 100000 && centerX < 900000) {
      // Likely UTM coordinates
      const estimatedLon = (centerX - 500000) / 111320 - 183; // Rough estimate
      const zoneNumber = Math.floor((estimatedLon + 180) / 6) + 1;
      const hemisphere = centerY > 5000000 ? "N" : "S";

      return {
        zone: zoneNumber,
        hemisphere: hemisphere,
        epsg: hemisphere === "N" ? 32600 + zoneNumber : 32700 + zoneNumber,
      };
    }

    return null;
  }

  static validateCoordinateSystem(coordinates, expectedSRS) {
    const validRanges = {
      "EPSG:4326": { x: [-180, 180], y: [-90, 90] },
      "EPSG:3857": {
        x: [-20037508.34, 20037508.34],
        y: [-20037508.34, 20037508.34],
      },
    };

    const range = validRanges[expectedSRS];
    if (!range) return true; // Can't validate unknown systems

    const [x, y] = coordinates;
    return (
      x >= range.x[0] && x <= range.x[1] && y >= range.y[0] && y <= range.y[1]
    );
  }
}

Best Practices#

Coordinate System Selection#

  1. Web Mapping: Use EPSG:3857 (Web Mercator) for tile-based maps

  2. Data Storage: Store original data in EPSG:4326 (WGS84) when possible

  3. Analysis: Use appropriate equal-area or conformal projections based on analysis type

  4. Regional Work: Use local coordinate systems (State Plane, UTM) for high accuracy

Data Handling#

// Best practices for coordinate handling
class CoordinateHandler {
  constructor(defaultSRS = 'EPSG:4326') {
    this.defaultSRS = defaultSRS;
    this.transformer = new CoordinateTransformer();
  }

  // Always validate input coordinates
  validateInput(coordinates, srs) {
    if (!Array.isArray(coordinates) || coordinates.length < 2) {
      throw new Error('Invalid coordinate format');
    }

    if (!CoordinateSystemDetector.validateCoordinateSystem(coordinates, srs)) {
      throw new Error(`Coordinates out of range for ${srs}`);
    }

    return true;
  }

  // Standardize coordinates to a common system
  standardize(coordinates, sourceSRS, targetSRS = this.defaultSRS) {
    this.validateInput(coordinates, sourceSRS);

    if (sourceSRS === targetSRS) {
      return coordinates;
    }

    return this.transformer.transform(coordinates, sourceSRS, targetSRS);
  }

  // Handle coordinate precision
  roundCoordinates(coordinates, precision = 6) {
    return coordinates.map(coord =>
      Math.round(coord * Math.pow(10, precision)) / Math.pow(10, precision)
    );
  }

  // Create coordinate metadata
  createMetadata(coordinates, srs, source = 'unknown') {
    return {
      coordinates: coordinates,
      srs: srs,
      precision: this.detectPrecision(coordinates),
      source: source,
      timestamp: new Date().toISOString()
    };
  }

  private detectPrecision(coordinates) {
    return Math.max(...coordinates.map(coord => {
      const str = coord.toString();
      const decimal = str.indexOf('.');
      return decimal === -1 ? 0 : str.length - decimal - 1;
    }));
  }
}

Common Pitfalls and Solutions#

  1. Axis Order: Some systems use (lat, lon) instead of (lon, lat)

  2. Unit Confusion: Always verify units (degrees vs. meters vs. feet)

  3. Datum Differences: NAD27 vs. NAD83 can differ by meters

  4. Precision Loss: Be careful with coordinate precision in transformations

  5. Bounds Checking: Always validate coordinates are within expected ranges

This reference provides the foundation for understanding and working with coordinate systems in Web GIS applications. Proper coordinate system handling is essential for accurate spatial analysis and mapping applications.