Skip to content

Chapter 3: Geographic Utility Functions#

Welcome back! In Chapter 1: Data Types & Models, we learned about the blueprints (data types) for our project's information, like geographic shapes and user inputs. In Chapter 2: Building Data API Client, we discovered how to fetch building data, including those geographic shapes (GeoJSON coordinates), from an external service.

Now that we have the data – the digital outline of a building or land plot – what do we do with it? We need to perform calculations and manipulations on this geographic information.

Your Geographic Toolkit#

Imagine you are a surveyor or an urban planner on a digital map. You have the boundary lines of a property, but you need to figure out its size, where its exact center is, and then, based on some proposed building plans (like the number of floors and height per floor), calculate the new building's potential area, height, and volume.

This requires specialized tools – digital calculators and measurement tapes that understand latitude, longitude, and shapes on a map.

This is exactly what the Geographic Utility Functions provide in our building-height project. They are a collection of helper functions designed to perform these kinds of calculations and transformations on geographic data. Think of them as your digital toolkit for working with shapes and coordinates.

What Can Our Toolkit Do?#

Our geographic utility functions help us with tasks like:

  1. Measuring Areas: Calculate the size (area) of a geographic shape, like the land plot defined by the GeoJSON we fetched.
  2. Finding Centers: Determine the central point of a polygon. This is useful for positioning things on the map or in a 3D view.
  3. Calculating Building Metrics: Using the land area and user inputs (like desired floors and floor height), calculate the building's potential footprint area, total height, and volume.
  4. Handling Map Projections: Sometimes geographic data comes in different "coordinate systems." These functions can help convert coordinates between different systems so everything lines up correctly.

Let's focus on the main use case: taking the building shape data from the API and user inputs, and calculating the building's size metrics.

Calculating Building Metrics from Shape and Input#

The primary utility function for this task is computeGeoMatrics. This function takes the geographic coordinates of the land plot (which we got using the API client in Chapter 2) and the user's building plan inputs (which follow the UserInputs blueprint from Chapter 1), and calculates useful metrics.

Let's look at how you would use it:

import { computeGeoMatrics } from "./utils/geo-operations";

// Assume 'buildingCoordinates' came from the API Client (Chapter 2)
// It would look something like the 'coordinates' part of FileContents (Chapter 1)
const buildingCoordinates = [
  [
    [-0.1278, 51.5074], // Example coordinates for a simple square
    [-0.1278, 51.5075],
    [-0.1277, 51.5075],
    [-0.1277, 51.5074],
    [-0.1278, 51.5074], // Close the loop
  ],
];

// Assume 'userInput' came from the user (Chapter 1)
const userInput = {
  lotCoverage: 80, // User wants the building to cover 80% of the land
  floorNumber: 10, // User wants 10 floors
  floorHeight: 3, // User wants each floor to be 3 meters high
};

// Now, use the utility function to calculate the metrics!
const calculatedMetrics = computeGeoMatrics(
  buildingCoordinates,
  userInput.floorHeight,
  userInput.floorNumber,
  userInput.lotCoverage
);

console.log("Calculated Building Metrics:", calculatedMetrics);

When you run this code, the computeGeoMatrics function will take the coordinate data and the numbers from userInput, do some calculations, and return an object containing the results.

The output calculatedMetrics object would look something like this (the exact numbers depend on the coordinates):

// Example output (numbers will vary based on actual coordinates)
{
  center: {
    type: "Feature",
    geometry: { type: "Point", coordinates: [-0.12775, 51.50745] },
    properties: {}
  },
  landArea: 110.5, // Example: Land area in square meters
  buildingArea: 88.4, // Example: Building footprint (80% of land area)
  volume: 2652, // Example: Volume (buildingArea * floorHeight * floorNumber)
  buildingHeight: 30 // Example: Height (floorHeight * floorNumber)
}

This calculatedMetrics object now contains all the essential information about the building based on the land shape and the user's simple plan. This object follows the structure defined by the Metrics blueprint from Chapter 1: Data Types & Models.

Under the Hood: Inside computeGeoMatrics#

Let's peek inside the computeGeoMatrics function (found in src/utils/geo-operations.ts) to see how it performs these calculations.

The Process (Non-Code Walkthrough)#

Here's a simple sequence of what happens when computeGeoMatrics is called:

  1. Receive Inputs: The function gets the raw geographic coordinates (like the GeoJSON shape data) and the user's numbers (floor height, floor number, lot coverage percentage).
  2. Understand the Shape: It uses a specialized geographic library (@turf/turf) to interpret the raw coordinates as a recognizable shape (a polygon).
  3. Calculate Land Area: It asks the geographic library to calculate the area of that polygon shape.
  4. Perform Building Calculations: It takes the calculated land area and uses basic arithmetic (multiplication, division) with the user's floor height, floor number, and lot coverage percentage to determine the building's potential footprint area, total height, and total volume.
  5. Find the Center: It asks the geographic library again to find the geographic center point of the original polygon shape.
  6. Package Results: It gathers all the calculated values (center, land area, building area, volume, height) into a single, structured object.
  7. Return Results: It sends the structured object back to whoever called the function.

Here's a simple diagram of this flow:

sequenceDiagram
    participant Caller as Code Calling Function
    participant Cgm as computeGeoMatrics Function
    participant Turf as @turf/turf Library

    Caller->>Cgm: Call computeGeoMatrics(coords, fh, fn, lc)
    Cgm->>Turf: Convert coords to Polygon
    Cgm->>Turf: Calculate Area of Polygon
    Turf-->>Cgm: Return Land Area
    Cgm->>Cgm: Calculate Building Area, Height, Volume (using Land Area, fh, fn, lc)
    Cgm->>Turf: Calculate Center of Polygon
    Turf-->>Cgm: Return Center Point
    Cgm->>Cgm: Package all results into an object
    Cgm-->>Caller: Return Results Object

Code Details#

Let's look at snippets from the src/utils/geo-operations.ts file that make this happen.

First, we need to import the necessary tools from the @turf/turf library:

// src/utils/geo-operations.ts
import { polygon, area, centerOfMass } from "@turf/turf";

// ... rest of the file ...

This line brings in specific functions: polygon (to create a polygon object from coordinates), area (to calculate the area of a polygon), and centerOfMass (to find the center).

Next, the start of the computeGeoMatrics function:

// src/utils/geo-operations.ts
export const computeGeoMatrics = (
  coordinates: any, // The shape data (like GeoJSON coords)
  floorHeight: number, // User input
  floorNumber: number, // User input
  lotCoverage: number // User input
) => {
  // Create a polygon object from the input coordinates
  const areaPolygon = polygon(coordinates);

  // Calculate the land area of the polygon
  const landArea = area(areaPolygon);

  // ... rest of the calculations ...
};

Here, polygon(coordinates) converts the coordinate data into a format that the @turf/turf library understands as a shape. Then, area(areaPolygon) performs the calculation to get the size of that land plot.

Now, the simple math for building metrics:

// src/utils/geo-operations.ts (inside computeGeoMatrics)
  // ... area calculation ...

  // Basic math using land area and user inputs
  const buildingHeight = floorHeight * floorNumber;
  const buildingArea = landArea * (lotCoverage / 100); // lotCoverage is a percentage
  const volume = landArea * (lotCoverage / 100) * floorHeight * floorNumber;
  // This is the same as buildingArea * buildingHeight

  // ... center calculation and return ...
};

These lines are straightforward multiplications. We use the calculated landArea and the user's floorHeight, floorNumber, and lotCoverage percentage to figure out the building's dimensions and volume. Notice the lotCoverage / 100 to convert the percentage into a decimal.

Finally, finding the center and returning the results:

// src/utils/geo-operations.ts (inside computeGeoMatrics)
  // ... building metrics calculations ...

  // Calculate the center point of the original polygon
  const center = centerOfMass(areaPolygon);

  // Return an object with all the results
  return { center, landArea, buildingArea, volume, buildingHeight };
};

centerOfMass(areaPolygon) calculates the central point of the shape. The function then packages center and all the previously calculated metrics (landArea, buildingArea, volume, buildingHeight) into a single object and returns it.

There's also a small helper function round in the actual code to keep the numbers tidy, but the core calculations are as shown above.

Other Geographic Helpers#

Besides computeGeoMatrics, the utils folder contains other geographic helpers, like those in src/utils/projection.ts.

Handling Map Projections (src/utils/projection.ts)#

Geographic data can sometimes be stored in different formats or "coordinate reference systems" (CRS). Think of it like using different types of maps (flat versus globe) or different grid systems. Sometimes, you need to convert coordinates from one system to another so they display or interact correctly.

The src/utils/projection.ts file uses the @math.gl/proj4 library to handle this.

// src/utils/projection.ts
import { Proj4Projection } from "@math.gl/proj4";

// Set up a projection tool to convert from EPSG:3857 to WGS84
export const positionProjection = new Proj4Projection({
  from: "EPSG:3857", // Web Mercator, common for web maps
  to: "WGS84", // Latitude/Longitude, common for GPS/GeoJSON
});

// ... function to apply this projection to data ...

This code sets up a tool (positionProjection) specifically for converting coordinates from one common web map system (EPSG:3857) to the standard system often used by GPS and GeoJSON (WGS84).

There's also a function transformLazData which uses this tool to convert a list of coordinates (like those from a point cloud, often used in 3D data) from one system to another. This is a more advanced use case, but the core idea is using positionProjection to convert points like positionProjection.project(vertex). These projection utilities ensure that map data, no matter its original format, can be correctly placed and used within our application, especially when integrating different data sources or preparing data for display in a map view.

Conclusion#

In this chapter, we explored the Geographic Utility Functions, our digital toolkit for working with map data. We learned how functions like computeGeoMatrics take geographic shapes (fetched by the API client) and user inputs (defined by our data types) to calculate important building metrics like area, volume, and height, as well as finding the center of a property. We also touched upon how other utilities handle tasks like converting coordinates between different mapping systems using projections.

These utility functions are crucial because they transform raw geographic data into usable information and metrics that the rest of the application can understand and display.

Now that we have the building geometry (from the API client) and the calculated metrics (from the utility functions), we have everything needed to visualize the building. In the next chapter, we'll dive into how the application takes this information and turns it into something you can see on the screen: the 3D building rendering pipeline.

Next Chapter: 3D Building Rendering Pipeline


Generated by AI Codebase Knowledge Builder