π Report Generation API
Report Generation API
A comprehensive Flask-based API for generating Phase I Environmental Site Assessment reports with integrated QuickBooks Online support, BigQuery data integration, and advanced spatial analysis capabilities.
Table of Contents
- Features
- Technology Stack
- Unified Tax Parcels Workflow
- API Endpoints
- Authentication & Health
- Client Management
- Project Management
- Report Compilation
- QuickBooks Integration
- Map & Spatial Data
- Quick Start
- API Usage Examples
- Map API Best Practices
- Authentication
- Data Mapping & Field Enhancements
- Architecture
- Development
- Testing
- Deployment
- API Changelog
Features
- Phase I Report Generation: Complete environmental assessment report creation with automated data collection
- Advanced Spatial Analysis: Distance-based property searches, geocoding, and GeoJSON mapping
- Dual Data Source Support: NYC PLUTO for city properties, NYS Tax Parcels for upstate locations
- City Directory Documents: Generate city directory sections using ACT project numbers
- QuickBooks Integration: Client management and proposal generation
- BigQuery Integration: Access to City_Directories, Database_Files, Sanborns, and PLUTO tables
- Google Cloud Authentication: Secure credential management with OAuth 2.0
- Document Download: Generated report file downloads
- Inspection Management: Google Calendar integration for scheduling site visits
- Site Diagrams: Automated generation with elevation data, building footprints, and property boundaries
Technology Stack
- Backend: Python 3.11, Flask
- Database: Google BigQuery
- Authentication: Google OAuth 2.0, QuickBooks OAuth 2.0
- Cloud Platform: Google Cloud Run, Google Cloud Storage
- Spatial Data: PostGIS-compatible geography types, Shapely for geometry operations
- Document Generation: python-docx, Adobe PDF Services API
- Mapping APIs: Google Maps API, ArcGIS Feature Services
- Dependencies: 29+ modern packages (see requirements.txt)
Unified Tax Parcels Workflow
The API uses a unified format for tax parcels across all jurisdictions (NYC, NYS, Nassau County, NJ, FL, Broome County). This simplifies the workflow:
- Get Nearby Lots: Call
GET /api/map/get_nearby_lots_by_addresswith an address - Unified Response: API returns lots with
TaxParcelsandJurisdictionfields in a consistent format - Add Project: Pass selected
TaxParcelsandJurisdictiontoPOST /api/project/add_project
Key Unified Fields:
- TaxParcels: Standardized parcel identifier (format varies by jurisdiction)
- Jurisdiction: One of "NYC", "NYS", "Nassau County", "NJ", "FL", "Broome County"
Example TaxParcels Formats by Jurisdiction:
- NYC: "3012340056" (10-digit BBL)
- NYS: "123.-4-56.7" (SBL)
- Nassau: "456.-78-90" (SBL)
- NJ: "12345678901234" (PAMS_PIN)
- FL: "12-34-56-789-0123" (PARCELNO)
- Broome: "123456" (CODE)
π See UNIFIED_TAXPARCELS_WORKFLOW.md for complete workflow documentation and examples.
API Endpoints
Authentication & Health
GET /health- API health check (no auth required)GET /auth_test- Test authentication with Google token (requires auth)
Client Management (/api/client)
GET /api/client/- Client API status checkPOST /api/client/add_client- Add new client to system and QuickBooks- Body:
{"ClientContact": "string", "ClientCompany": "string", "ClientAdress": "string", "ClientEmail": "string", "ClientPhone": "string", "ClientAddress": "string"} GET /api/client/client_info?ClientKey=<id>- Get client information by ClientKeyGET /api/client/client_info- Get all clients (no ClientKey parameter)GET /api/client/project_client_info?act_project_number=<id>- Get client information for a project- Parameters:
act_project_number(required) - Returns: Complete client details by looking up ClientKey from ACT_Projects table
- Features: Two-step lookup (projectβclient_key, client_keyβclient_info)
- Use Cases: Display client information in project details, populate client fields in reports
GET /api/client/health- Client module health check
Project Management (/api/project)
POST /api/project/add_project- Add new project with address and unified tax parcel identifier- Body:
{"Address": "string", "ClientKey": number, "Jurisdiction": "string", "Geography": {...}, "TaxParcels": "string" | ["string"], "Project_Type": "string"} - Required Fields:
Address: Full property address (required)ClientKey: Client identifier (required)Jurisdiction: Jurisdiction identifier -"NYC","NYS","Nassau County","NJ","FL", or"Broome County"(required)
-
Optional Fields:
Geography: GeoJSON geometry object - can be obtained fromget_nearby_lots_by_addressresponse (recommended)TaxParcels: Unified tax parcel identifier(s) fromget_nearby_lots_by_addressresponseProject_Type: Type of project (e.g., "Phase I ESA", "Commercial")
-
Geography Field (recommended - ensures accurate lot geometry):
- Source: Get from
get_nearby_lots_by_addressresponse - Format: GeoJSON geometry object(s) using WGS84 (EPSG:4326) coordinate system
- Coordinate Order:
[longitude, latitude](NOT latitude, longitude) - Formats Supported:
- Single Polygon:
json { "type": "Polygon", "coordinates": [ [ [-73.9851, 40.7589], // [longitude, latitude] [-73.9850, 40.7589], [-73.9850, 40.7590], [-73.9851, 40.7590], [-73.9851, 40.7589] // Must close the ring ] ] } - Array of Polygons (for multiple lots):
json [ {"type": "Polygon", "coordinates": [[[lng1, lat1], [lng2, lat2], ...]]}, {"type": "Polygon", "coordinates": [[[lng3, lat3], [lng4, lat4], ...]]} ] - FeatureCollection:
json { "type": "FeatureCollection", "features": [ { "type": "Feature", "geometry": {"type": "Polygon", "coordinates": [...]} } ] } - MultiPolygon:
json { "type": "MultiPolygon", "coordinates": [ [[[lng1, lat1], [lng2, lat2], ...]], [[[lng3, lat3], [lng4, lat4], ...]] ] } - Multiple Lots Handling:
- When array or FeatureCollection provided, all geometries are automatically merged into a single polygon boundary using
unary_union - Resulting merged polygon is stored as the project's Geography in BigQuery
- This creates a unified property boundary for multi-lot projects
- Benefits:
- Guarantees exact lot polygon instead of geocoded point
- Handles complex multi-lot properties seamlessly
- No manual geometry merging required
- Priority: If provided, Geography is used directly (TaxParcels lookup skipped)
- Recommended Workflow:
- Call
get_nearby_lots_by_addressto find lots at the project address - User selects one or multiple lots from results
- Pass selected lot geometries (single or array) to
add_projectGeography field - System automatically merges multiple lots into single boundary and stores in BigQuery
-
Example (Single Lot):
json { "Address": "111 Huntington Road, Port Washington, NY, 11050", "ClientKey": 262, "Jurisdiction": "Nassau County", "Geography": { "type": "Polygon", "coordinates": [[[-73.699, 40.823], ...]] }, "TaxParcels": "05006100040000000000" } -
Example (Multiple Lots - Array Format):
json // User selected 2 adjacent lots from get_nearby_lots_by_address // System will automatically merge both polygons into single boundary { "Address": "123 Main St, Brooklyn, NY 11201", "ClientKey": 456, "Jurisdiction": "NYC", "Geography": [ {"type": "Polygon", "coordinates": [[[-73.985, 40.693], ...]]}, {"type": "Polygon", "coordinates": [[[-73.986, 40.694], ...]]} ], "TaxParcels": ["3012340056", "3012340057"] } -
Example (Multiple Lots - FeatureCollection Format):
json // Alternative format for multiple lots (from GIS systems) // All features are merged into single project boundary { "Address": "456 Oak Ave, Queens, NY 11375", "ClientKey": 789, "Jurisdiction": "NYC", "Geography": { "type": "FeatureCollection", "features": [ {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [...]}}, {"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [...]}} ] }, "TaxParcels": ["4023450078", "4023450079"] }
- Source: Get from
-
TaxParcels Format (unified across jurisdictions):
- Get the
TaxParcelsvalue fromget_nearby_lots_by_addressresponse - NYC:
"3012340056"(10-digit BBL) - NYS:
"123.-4-56.7"(SBL format) - Nassau County:
"05006100040000000000"(SBL/PRINT_KEY format) - NJ:
"12345678901234"(PAMS_PIN) - FL:
"12-34-56-789-0123"(PARCELNO) - Broome County:
"123456"(CODE) - Multiple Parcels: Array of strings (e.g.,
["3012340056", "3012340057"]) - Can be omitted if Geography provided: System will use provided geometry directly
- Get the
-
Auto-Detection & Data Fetching (when Geography not provided):
- NYC (with BBL): Queries MAPPLUTO API directly using 10-digit BBL
- NYC (no BBL): Uses NYC Geoclient API to get BBL from address, then fetches from MAPPLUTO
- NYS: Geocodes address β queries NYS Tax Parcels API β extracts SBL
- Nassau County (with TaxParcels): Queries Nassau County Parcels v2 API using SBL/PRINT_KEY
- Nassau County (no TaxParcels): Geocodes address β queries Nassau County API β extracts SBL
- New Jersey: Geocodes address β queries NJ MOD IV API β extracts PAMS_PIN
- Florida: Geocodes address β queries FL Cadastral API β extracts PARCELNO
-
Features:
- Geography Priority: If Geography provided, used directly (most accurate)
- Multiple Lots Support: Accepts array of geometries or FeatureCollection, automatically merged with
unary_union - Simplified Input: Only address and client key required - parcel IDs optional
- Smart Auto-Detection: Automatically detects jurisdiction from address geocoding
- Multi-Jurisdiction Support: Handles NYC, NYS, Nassau, NJ, FL automatically
- Automatic Geography: Fetches property boundary polygon from jurisdiction-specific API
- Parcel ID Extraction: Auto-extracts official identifiers (BBL, SBL, PAMS_PIN, PARCELNO)
- TaxParcels Storage: Stores extracted parcel identifiers in TaxParcels array field
- Site Diagram Initialization: Creates initial site diagram entry with subject property (NYC only)
-
Example Requests: ```json // Simple NYC project (auto-detects BBL from address) { "Address": "123 Main St, Brooklyn, NY 11201", "ClientKey": 456, "Jurisdiction": "NYC" }
// NYS with TaxParcels only (will fetch geometry) { "Address": "100 State Street, Albany, NY 12207", "ClientKey": 789, "Jurisdiction": "NYS", "TaxParcels": "123.-4-56.7" }
// New Jersey with TaxParcels { "Address": "100 Main St, Newark, NJ 07102", "ClientKey": 999, "Jurisdiction": "NJ", "TaxParcels": "12345678901234" }
// Multiple NYC parcels { "Address": "123 Main St, Brooklyn, NY 11201", "ClientKey": 456, "Jurisdiction": "NYC", "TaxParcels": ["3012340056", "3012340057"] }
// Without TaxParcels (auto-detected from address) { "Address": "123 Main St, Brooklyn, NY 11201", "ClientKey": 456, "Jurisdiction": "NYC" } ```
-
Returns:
{"status": "success", "ACT_Project_Number": "string", "site_diagram_created": boolean, "site_diagram_message": "string"} - Next Steps: Use
/set_adjacentsto add adjacent properties,/create_site_diagramto generate full layers (buildings, curbs, elevation) POST /api/project/search-by-location- Search for projects within a geographic radius- Body:
{"latitude": 40.7589, "longitude": -73.9851, "radiusMeters": 500} - Returns: Array of projects within specified radius, sorted by distance
- Features:
- Uses BigQuery Geography functions (ST_DISTANCE) for accurate distance calculations
- Searches the Geography column from ACT_Projects table
- Default radius: 500 meters (configurable 0-50km)
- Results include project details, distance in meters, sorted nearest to farthest
- Limit: 100 projects maximum
- Use Cases: Project map searches, nearby project discovery, location-based project filtering
POST /api/project/set_adjacents- Set or update adjacent properties for a project- Body:
{"ACT_Project_Number": integer, "adjacents": [{"BBL": {"Borough": integer, "Block": integer, "Lot": integer}, "adjacentNumber": integer}]} - Features:
- Unified Upsert: Uses
upsert_site_diagram_with_data()to intelligently merge with existing site diagram - Preserves Subject Property: Property_Boundary and other subject property layers remain unchanged
- BBL Validation: Validates Borough/Block/Lot format and values
- Auto-fetch from MAPPLUTO: Automatically retrieves geometry, owner, and addresses from ArcGIS MAPPLUTO API
- Geography Support: Stores property geometries as WKT POLYGON format
- Versioning: Creates new version of site diagram while marking previous as 'outdated'
- BBL Format: Borough (1-5), Block (1-99999), Lot (1-9999) β stored as complete struct with all property data
- Unified Upsert: Uses
- Returns:
{"success": true, "ACT_Project_Number": 10890, "version": 2, "adjacents_count": 3, "operation": "updated", "additional_processing": {...}} - Example:
json { "ACT_Project_Number": 10890, "adjacents": [ { "BBL": {"Borough": 3, "Block": 2127, "Lot": 10}, "adjacentNumber": 1 }, { "BBL": {"Borough": 3, "Block": 2127, "Lot": 12}, "adjacentNumber": 2 } ] }
Site Diagram API (Unified Architecture)
Overview: Site diagrams use a unified upsert pattern that intelligently manages subject property and adjacent property data:
- Property_Boundary (and other layer columns) = Subject property geographic layers
- adjacents array = Adjacent properties information
- Automatically created during add_project, updated via set_adjacents or upsert_site_diagram
Core Endpoints:
- POST /api/project/upsert_site_diagram - [NEW] Create or update site diagram from project data
- Body: {"ACT_Project_Number": 11099}
- Purpose: Unified endpoint that creates/updates site diagram using data from ACT_Projects table
- Features:
- Automatically fetches subject property Geography and BBL from ACT_Projects
- Updates Property_Boundary with subject property geometry
- Preserves existing adjacents if present
- Handles versioning and streaming buffer issues
- Can be called at any time to refresh site diagram from project data
- Returns: {"success": true, "ACT_Project_Number": 11099, "version": 1, "subject_property": "updated", "adjacents_count": 0, "operation": "created"}
- Use Cases: Manual site diagram creation, refresh after project updates, iOS app initialization
POST /api/project/generate_site_diagram_with_elevation- Generate and store complete site diagram with elevation data- Body:
{"ACT_Project_Number": "string"} - Purpose: Creates all core data types (property boundary, building footprints, curb lines, elevation raster) and stores in BigQuery with Google Cloud Storage
- Returns:
{"success": true, "bigquery_row_id": "string", "message": "Site diagram generated and stored successfully"} -
Features: Real NYC DEM 2017 1-foot LiDAR data, 12 color-coded elevation bands, detailed elevation metadata, GCS storage for raster images
-
GET /api/project/get_site_diagram- Retrieve stored site diagram data - Parameters:
ACT_Project_Number(required) - Purpose: Returns all stored GeoJSON layers and elevation raster data for iOS app consumption
- Returns: Complete site diagram with detailed elevation metadata including elevation bands, color mapping, pixel counts, and statistical information
- Data Types: Property boundary (Polygon), building footprints (Polygon), curb lines (MultiLineString), elevation raster (PNG with metadata)
Unified Internal Function: upsert_site_diagram_with_data()
- Used internally by add_project, set_adjacents, and upsert_site_diagram endpoints
- Intelligently merges subject property data and/or adjacents data
- Parameters:
- subject_property_data: Dict with geo (WKT), borough, block, lot, owner, addresses
- adjacents_data: List of dicts with adjacent property info
- generated_by: Source identifier (e.g., 'project_creation', 'set_adjacents_api', 'upsert_api')
- Behavior:
- On add_project: Populates Property_Boundary from Geography, adjacents=NULL
- On set_adjacents: Updates adjacents array, preserves Property_Boundary
- On upsert_site_diagram: Updates subject property from ACT_Projects, preserves adjacents
- Handles versioning automatically (increments version on updates)
- Marks old versions as 'outdated', new version as 'active'
- Works around BigQuery streaming buffer limitations
Legacy Site Diagram (Deprecated)
POST /api/project/generate_site_diagram- Generate site diagram for existing project (Legacy)- Body:
{"ACT_Project_Number": "string"} - Returns: Site diagram files in multiple GeoJSON layers plus composite PNG diagram
- Status: β οΈ DEPRECATED - Use two-endpoint architecture above for iOS applications
POST /api/project/schedule_inspection- Schedule inspection for a project- Body:
{"ACT_Project_Number": "string", "assignee_emails": ["string"], "date": "YYYY-MM-DDTHH:MM", "site_contact_name": "string", "site_contact_email": "string", "description": "string"} - Returns:
{"status": "success", "event_id": "string", "calendar_id": "string"} PUT /api/project/update_inspection- Update existing inspection- Body:
{"event_id": "string", "ACT_Project_Number": "string", "assignee_emails": ["string"], "date": "YYYY-MM-DDTHH:MM", "site_contact_name": "string", "site_contact_email": "string", "description": "string"} DELETE /api/project/delete_inspection- Delete inspection- Body:
{"event_id": "string", "send_cancellation": boolean} POST /api/project/preview_project_data- Preview project data before report generation- Body:
{"ACT_Project_Number": "string"}
Report Compilation (/api/compile)
PDF Report Compilation with Advanced Processing
POST /api/compile/compile_report/<project_number>- Compile complete PDF report with appendices- Headers:
Authorization: Bearer <token>(Required for production) - Purpose: Creates final PDF with all appendices, photos, historical research, and database files
- Request Body (optional):
json { "compress_pdf": false, // Default: false - no compression for best quality "files_project_number": "10890" // Optional: Use files from a different project } - Cross-Project File Lookup: The
files_project_numberparameter allows report compilation to pull appendix files (aerials, sanborns, database files, city directories) from a different ACT project number than the main report. Useful for shared sites or multi-phase projects. - Response:
json { "success": true, "pdf_url": "gs://compiled_reports/project_123/Phase_I_Report_123.pdf", "file_size_mb": 15.2, "pages": 45, "compilation_time_seconds": 67.5, "appendices_included": { "photos": "embedded_in_template", "aerials": true, "database_files": true } } - Notes:
- Word document template automatically found in Google Drive
- Photos are embedded directly in Word template (no separate appendix)
- Aerials/historical documents and database files always included
Advanced Photo Processing Features
- Portrait Rotation: Automatically detects and rotates portrait photos to landscape during Word document creation
- Quality Optimization: Smart compression balancing file size and image quality
- EXIF Handling: Proper orientation correction from camera metadata
- Batch Processing: Efficient handling of multiple photo appendices per project
Intelligent Appendix Management
- Insertion-Based Processing: Inserts content into existing appendix sections rather than appending
- Duplicate Prevention: Smart filtering prevents duplicate historical research files
- File Type Preferences: Prioritizes PDF files over Excel files for database content
- Size Optimization: Automatic file size management without quality loss
Photo Generation (/api/report)
POST /api/report/generate_photo_files_debug- Generate optimized photo documents- Headers:
Authorization: Bearer <token>(Required for production) - Body:
{"ACT_Project_Number": "string"} - Features:
- Portrait photo rotation to landscape orientation
- EXIF orientation correction
- Smart compression with quality preservation
- Form-based photo organization with captions
Two-Step Report Generation Workflow (Recommended)
Step 1: Get Report Variables for Review
- POST /api/report/get_report_variables - Fetch and map all template variables without creating report
- Body: {"ACT_Project_Number": "string"}
- Purpose: Allows users to review and edit variables before report generation
- Returns: Complete field mappings, data sources, unmapped fields, and editable variable structure
- Benefits: Data validation, manual override capability, blank field identification
Step 2: Create Report from Reviewed Variables
- POST /api/report/create_report_from_variables - Generate report using reviewed/edited variables
- Body: {"variables": {...}, "ACT_Project_Number": "string"}
- Purpose: Creates final report from user-reviewed variable set
- Features: Uses provided variables exactly as specified, maintains data integrity, supports all manual overrides
Legacy Single-Step Generation (Deprecated)
POST /api/report/create_project_report- Create comprehensive Phase I report (legacy)- Body:
{"ACT_Project_Number": "string"} - Status: β οΈ DEPRECATED - Use two-step workflow above for better control
- Enhanced Features:
- Complete building dimension mapping (area, frontage, depth)
- Normalized location data (full borough names, specific neighborhoods)
- Real cross street detection via Google Maps API
- Construction material details from form submissions
- City directory integration (Section 5.5)
Document Generation & Downloads
POST /api/report/generate_city_directory_document- Generate city directory document (standalone)- Body:
{"act_project_number": "string", "adjacent_north": "string", "adjacent_south": "string", "adjacent_east": "string", "adjacent_west": "string"} - Note: City directory data is now automatically included in all reports as Section 5.5
- City directory integration (Section 5.5)
GET /api/report/download_city_directory_by_project?ACT_Project_Number=<id>- Download city directory by projectGET /api/report/list_city_directories- List all available city directory documentsGET /api/report/download_report?filename=<name>- Download report by filenameGET /api/report/download_report_by_project?ACT_Project_Number=<id>- Download report by project numberGET /api/report/list_reports- List all available reportsGET /api/report/get_city_directories?act_project_number=<id>- Get city directory dataGET /api/report/get_database_files?act_project_number=<id>- Get database filesGET /api/report/get_sanborns?act_project_number=<id>- Get Sanborn mapsGET /api/report/get_all_project_data?act_project_number=<id>- Get comprehensive project data
QuickBooks Integration (/api/qb)
GET /api/qb/qb_auth_url- Get QuickBooks authorization URLGET /api/qb/qb_callback?code=<code>&state=<state>&realmId=<realm>- Handle QuickBooks OAuth callbackGET /api/qb/qb_company_info- Get QuickBooks company informationGET /api/qb/qb_customers- Get all QuickBooks customersGET /api/qb/qb_customer/<customer_id>- Get specific QuickBooks customer by IDPOST /api/qb/qb_create_customer- Create new customer in QuickBooks- Body:
{"Name": "string", "CompanyName": "string", "BillAddr": {"Line1": "string", "City": "string", "CountrySubDivisionCode": "string", "PostalCode": "string"}, "PrimaryEmailAddr": {"Address": "string"}, "PrimaryPhone": {"FreeFormNumber": "string"}} GET /api/qb/qb_items- Get all QuickBooks items/services
Map & Spatial Data (/api/map)
Aerial Maps Generation (/api/aerial)
POST /api/aerial/generate_aerial_maps- Generate historical aerial maps for a project- Body:
{"ACT_Project_Number": "string"} - Data Sources:
- NYC: NYC.gov tile service (XYZ format) - https://maps.nyc.gov/xyz/1.0.0/photo/{year}/{z}/{x}/{y}.png8
- Non-NYC: NYS Orthos WMS service - https://orthos.its.ny.gov/arcgis/services/wms/{year}/MapServer/WMSServer
- Historical Coverage: 12-25 aerial images depending on location
- NYC Years: 2024, 2022, 2020, 2018, 2016, 2014, 2012, 2010, 2008, 2006, 2004, 2001-2, 1996, 1951, 1924
- NYS Years: 2000-2025 (fills gaps in NYC coverage)
- Image Specifications:
- Zoom Level: 22 (highest detail)
- Tile Grid: 5Γ5 tiles (1280Γ1280 pixels)
- Format: PNG8 for NYC, PNG for NYS
- Blank Image Detection: Automatically filters out images with no coverage
- Output:
- Individual PDF per year with labeled map
- Compiled multi-page PDF with all available years
- Stored in Google Cloud Storage:
gs://act_aerials/project_{number}/ - BigQuery tracking:
Phase_I_Report.Aerialstable
- Features:
- Automatic NYC/non-NYC detection based on project BBL
- Comprehensive year coverage combining both data sources
- ILoveAPI PDF merging for final compilation
- Letter-sized pages with year labels
- Smart image validation and error handling
- Response:
json { "status": "success", "project_number": "10991", "maps_generated": 12, "location_type": "NYC", "years_processed": ["2024", "2022", "2020", ...], "pdf_url": "gs://act_aerials/project_10991/aerials_10991_20251105_012212.pdf", "bigquery_inserted": true } - Use Cases: Phase I environmental reports, historical site analysis, temporal change detection
Property Boundary & Project Data
GET /api/map/get_adjacents_geojson?act_project_number=<id>- Get adjacent properties in GeoJSON format- Parameters:
act_project_number(required) - Returns: GeoJSON FeatureCollection with BBL, adjacentNumber, direction, and geometry data
- Features: WKT geometry conversion, error handling for invalid geometries, BBL string formatting
-
Use Cases: Phase I environmental reports, adjacent property analysis, boundary surveys
-
GET /api/map/get_projects_geojson?act_project_number=<id>- Get specific project in GeoJSON format - Parameters:
act_project_number(optional - if omitted, returns all projects) - Returns: GeoJSON FeatureCollection with project data, BBL arrays, and geography
- Features: Multiple BBL support, geography WKT conversion, client information integration
-
Use Cases: Single project visualization, site location mapping, project boundaries
-
GET /api/map/get_projects_geojson- Get all projects in GeoJSON format for mapping overview - Returns: GeoJSON FeatureCollection with all projects data ordered by project number
- Features: Complete project dataset for mapping applications, bulk spatial data export
- Use Cases: Portfolio mapping, regional analysis, project distribution visualization
Distance-Based Spatial Queries
GET /api/map/get_nearby_lots_geojson?act_project_number=<id>&distance_feet=<distance>- Get lots within specified distance of project- Parameters:
act_project_number(required): Project ID to search arounddistance_feet(optional, default: 500): Search radius in feet
- Returns: GeoJSON FeatureCollection with nearby lots and property details
- Subject Lot Identification: Each lot includes
is_subject_lotflagis_subject_lot: true- Lot(s) that match the project's BBL (NYC) or intersect with project geography (non-NYC)is_subject_lot: false- All other lots in the search radius- NYC: Uses BBL matching (Borough-Block-Lot comparison)
- Non-NYC: Uses geometry intersection detection
- Multiple lots can be marked as subject if project has multiple BBLs
- Data Sources:
- NYC: PLUTO/MapPLUTO database
- Non-NYC: NYS Tax Parcels ArcGIS service
- Features:
- Uses project's Geography field from ACT_Projects (no geocoding)
- Spatial proximity analysis using ST_DWITHIN
- Automatic NYC/non-NYC detection based on project BBL
- Includes project's own lots in results (marked with is_subject_lot: true)
- Distance calculations in both meters and feet
- Up to 200 nearest lots returned
- Property details include owner, building class, lot area, zoning, etc.
- NYC Response Fields: BBL (Borough-Block-Lot), bbl_string, address, owner_name, building_class, lot_area, building_area, num_floors, year_built, zone_district, is_subject_lot
- Non-NYC Response Fields: lot_number (SBL), SWIS, address, owner, property_class, land_value, total_value, acreage, county, city, is_subject_lot
-
Use Cases: Environmental radius searches, neighborhood analysis, property research within specific distance
-
GET /api/map/get_nearby_lots_by_address?address=<address>&distance_feet=<distance>- Find lots near any geocoded address with unified TaxParcels format - Parameters:
address(required): Any valid address to geocode and search arounddistance_feet(optional, default: 200): Search radius in feet
- Returns: GeoJSON FeatureCollection with nearby lot data in unified format
- Unified Response Fields (for all jurisdictions):
TaxParcels: Standardized parcel identifier (format varies by jurisdiction)Jurisdiction: One of"NYC","NYS","Nassau County","NJ","FL","Broome County"
- Legacy Identifiers (still included for backward compatibility):
- NYC:
BBLobject{Borough, Block, Lot}andbbl_string - Non-NYC:
lot_number(SBL - Section-Block-Lot identifier)
- NYC:
- Subject Lot Identification: Each lot includes
is_subject_lotflagis_subject_lot: true- Lot that contains the geocoded address pointis_subject_lot: false- All other lots in the search radius- Uses point-in-polygon detection (Shapely) for accurate identification
- Only one lot will be marked as subject lot per query
- Data Sources:
- NYC addresses: PLUTO/MapPLUTO database
- Non-NYC addresses: NYS Tax Parcels ArcGIS Feature Service
- Features:
- Automatic Geocoding: Converts address to lat/lon using Google Maps API
- Smart Location Detection: Automatically determines NYC vs non-NYC based on:
- Address components (locality, sublocality)
- Administrative area (county names)
- NYC borough detection (Manhattan, Bronx, Brooklyn, Queens, Staten Island)
- Dual Data Source: Seamlessly switches between NYC PLUTO and NYS Tax Parcels
- Distance Calculations: Returns distance in both meters and feet for each lot
- Complete Geometries: Full GeoJSON polygon geometries for mapping
- Sorted Results: Lots ordered by distance (nearest first)
- Result Limit: Up to 200 lots within specified radius
- Response Includes:
query_address: Original address from requestformatted_address: Google's standardized address formatgeocoded_location: Precise lat/lon coordinatesis_nyc: Boolean indicating NYC vs non-NYCborough: NYC borough name if applicable (Manhattan, Brooklyn, etc.)data_source: "NYC PLUTO" or "NYS Tax Parcels"geojson: Complete FeatureCollection with lot features
- NYC Lot Properties: BBL object
{Borough, Block, Lot}, bbl_string (10-digit), address, owner_name, building_class, lot_area, building_area, num_floors, year_built, zone_district, distance_meters, distance_feet, is_subject_lot - Non-NYC Lot Properties: lot_number (SBL), SWIS, SWIS_SBL_ID, address, street_number, street_name, city, county, zip, owner, property_class, land_value, total_value, acreage, year_built, square_feet, distance_meters, distance_feet, is_subject_lot
- Use Cases:
- Property research without existing project
- Market analysis around target address
- Site selection and property scouting
- Ad-hoc spatial queries for any location
- Finding available lots near desired address
- Environmental due diligence for new sites
- Example Addresses:
- NYC:
"350 5th Ave, New York, NY 10118"(Empire State Building) - Non-NYC:
"100 State St, Albany, NY 12207"(NYS Capitol area) - Informal:
"Times Square, New York, NY"
- NYC:
- Error Handling:
- Invalid address: Returns 404 with geocode status
- Missing address parameter: Returns 400 error
- Invalid distance_feet: Returns 400 error
- Geocoding failure: Returns 500 with detailed error message
Quick Start
Prerequisites
- Google Cloud Project with BigQuery enabled
- QuickBooks Developer Account (for QB integration)
- Google OAuth 2.0 credentials
Environment Setup
-
Clone and Install
bash git clone <repository-url> cd Report_Generation python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt -
Configure Environment Variables
bash export GCP_PROJECT_ID="your-gcp-project-id" export GOOGLE_CLIENT_ID="your-google-oauth-client-id" export QB_CLIENT_ID="your-quickbooks-client-id" export QB_CLIENT_SECRET="your-quickbooks-client-secret" -
Run Locally
bash python app.py # Or using the Flask task chmod +x run_flask.sh ./run_flask.sh
Docker Deployment
# Build image
docker build -t report-generation-api .
# Run container
docker run -p 8080:8080 \
-e GCP_PROJECT_ID="your-project-id" \
-e GOOGLE_CLIENT_ID="your-client-id" \
report-generation-api
Google Cloud Run Deployment
# Deploy to Cloud Run
gcloud run deploy report-generation-api \
--source . \
--platform managed \
--region your-region \
--allow-unauthenticated
API Usage Examples
Aerial Maps Generation
Generate Historical Aerial Maps for a Project
curl -X POST "https://your-api-url/api/aerial/generate_aerial_maps" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "10991"
}'
Response Example:
{
"status": "success",
"project_number": "10991",
"maps_generated": 12,
"location_type": "NYC",
"years_processed": [
"2024", "2022", "2020", "2018", "2016",
"2014", "2012", "2010", "2008", "2006",
"2004", "2001-2"
],
"skipped_years": ["1996", "1951", "1924"],
"skipped_reason": "No image coverage available",
"pdf_url": "gs://act_aerials/project_10991/aerials_10991_20251105_012212.pdf",
"bigquery_inserted": true,
"processing_details": {
"zoom_level": 22,
"tile_grid": "5x5",
"image_size": "1280x1280 pixels",
"pdf_page_size": "Letter (8.5x11 inches)",
"total_tiles_fetched": 300,
"blank_images_filtered": 15
}
}
Features:
- Automatically detects NYC vs non-NYC based on project BBL
- Fetches up to 25 historical years (12-25 maps depending on coverage)
- NYC: High-resolution tile service (zoom 22, 5Γ5 grid)
- Non-NYC: NYS Orthos WMS with blank image detection
- Compiled multi-page PDF stored in GCS bucket act_aerials
- BigQuery tracking in Phase_I_Report.Aerials table
Site Diagram API for iOS Apps (Two-Endpoint Architecture)
Step 1: Generate and Store Site Diagram with Elevation Data
curl -X POST "https://your-api-url/api/project/generate_site_diagram_with_elevation" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "10918"
}'
Response Example:
{
"success": true,
"bigquery_row_id": "10918_v1",
"message": "Site diagram generated and stored successfully",
"data_types_generated": [
"property_boundary",
"building_footprints",
"curb_lines",
"elevation_raster"
]
}
Step 2: Retrieve Site Diagram for iOS App
curl -X GET "https://your-api-url/api/project/get_site_diagram?ACT_Project_Number=10918" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Response Example (iOS-Ready Format):
{
"ACT_Project_Number": "10918",
"success": true,
"status": "active",
"version": 1,
"created_at": "2025-09-12T23:50:23.850741+00:00",
"generated_by": "api_request",
"data_availability": {
"property_boundary": true,
"building_footprints": true,
"curb_lines": true,
"elevation_raster": true
},
"project_info": {
"address": "5818 5th Ave, Brooklyn, NY",
"bbl": [{"Borough": "BK", "Block": "855", "Lot": "44"}],
"center_coordinates": {
"latitude": 40.64038907013211,
"longitude": -74.0156322709292
}
},
"metadata": {
"api_version": "2.0",
"core_data_types": ["property_boundary", "building_footprints", "curb_lines", "elevation_raster"],
"elevation_raster_status": "generated",
"generation_method": "api_core_data_with_elevation_raster"
},
"site_diagram_data": {
"property_boundary": {
"type": "Polygon",
"coordinates": [[[longitude, latitude], ...]]
},
"building_footprints": {
"type": "Polygon",
"coordinates": [[[longitude, latitude], ...]]
},
"curb_lines": {
"type": "MultiLineString",
"coordinates": [[[longitude, latitude], ...]]
},
"elevation_raster": {
"url": "https://storage.googleapis.com/act-phase-i-site-diagrams/elevation_rasters/project_10918_elevation_v20250912_195021.png",
"bounds": [-74.015797, 40.640282, -74.015468, 40.640496],
"width_pixels": 512,
"height_pixels": 512,
"resolution_meters": 0.07,
"image_format": "PNG",
"color_scheme": "topographic_gradient",
"dem_source": "NYC DEM 2017 1-foot LiDAR",
"processing_method": "nyc_dem_real_data_pil_color_coding",
"elevation_stats": {
"min": 99.09,
"max": 122.44,
"range": 23.35
},
"elevation_bands": [
{
"band_id": 1,
"elevation_min": 99.1,
"elevation_max": 101,
"color_hex": "#000080",
"pixel_count": 538
},
{
"band_id": 2,
"elevation_min": 101,
"elevation_max": 103,
"color_hex": "#0040FF",
"pixel_count": 889
},
// ... 10 more elevation bands with detailed color mapping
]
}
}
}
iOS Integration Benefits
- All Data in Single Response: Property boundaries, building footprints, curb lines, and elevation raster in one API call
- Real Elevation Data: NYC DEM 2017 1-foot LiDAR with 12 color-coded elevation bands
- Detailed Metadata: Elevation statistics, color mapping, and pixel counts for legend creation
- Optimized for Mobile: Lightweight GeoJSON format with direct image URLs
- Storage Efficiency: Raster images stored in Google Cloud Storage with public URLs
- Version Control: BigQuery storage with version tracking and retrieval capabilities
PDF Report Compilation (Primary Workflow)
Compile Complete PDF Report with All Appendices
curl -X POST "https://your-api-url/api/compile/compile_report/123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
Default Behavior (Recommended): - Word document template automatically found in Google Drive - Photos embedded directly in Word template (no separate appendix) - All historical research files (aerials) always included - All database search results always included - No compression applied for best quality - Smart duplicate filtering and file preferences
Custom Compilation Options (Compression Only)
curl -X POST "https://your-api-url/api/compile/compile_report/123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"compress_pdf": true
}'
Response Example
{
"success": true,
"pdf_url": "gs://compiled_reports/project_123/Phase_I_Report_123.pdf",
"file_size_mb": 15.2,
"pages": 45,
"compilation_time_seconds": 67.5,
"appendices_included": {
"photos": "embedded_in_template",
"aerials": true,
"database_files": true
}
}
Two-Step Report Generation (Recommended)
Step 1: Get Report Variables for Review
curl -X POST "https://your-api-url/api/report/get_report_variables" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "123"
}'
Response includes:
- Complete variable mappings for template
- Data sources and coverage analysis
- Unmapped/blank fields identification
- Field edit capabilities
Step 2: Create Report from Reviewed Variables
curl -X POST "https://your-api-url/api/report/create_report_from_variables" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "123",
"variables": {
"PropertyAddress": "123 Main Street, Brooklyn, NY 11201",
"ActProject": "123",
"ClientContact": "John Doe",
"Elevation": "Custom elevation description",
"BuildingArea": "2,500 sq ft"
}
}'
Benefits of Two-Step Workflow: - Review all variables before report generation - Edit any field with custom values - Identify and fill blank/missing fields - Better data validation and quality control - Maintain audit trail of data sources
Legacy Single-Step Report Generation (Deprecated)
Create Phase I Report (Legacy Method)
curl -X POST "https://your-api-url/api/report/create_project_report" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "123"
}'
β οΈ Note: This legacy endpoint is deprecated. Use the two-step workflow above for better control and data validation.
Client and Project Management
Add a New Client
curl -X POST "https://your-api-url/api/client/add_client" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ClientContact": "John Doe",
"ClientCompany": "Example Corp",
"ClientAdress": "123 Main St",
"ClientEmail": "john@example.com",
"ClientPhone": "555-1234",
"ClientAddress": "123 Main St, New York, NY"
}'
Add a New Project
curl -X POST "https://your-api-url/api/project/add_project" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Address": "123 Main Street, New York, NY",
"ClientKey": 1,
"BBL": [
{
"Borough": "1",
"Block": "123",
"Lot": "45"
}
],
"TaxID": "12345",
"Project_Type": "Phase I Environmental Site Assessment"
}'
Set Adjacent Properties for a Project
curl -X POST "https://your-api-url/api/project/set_adjacents" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": 10890,
"adjacents": [
{
"BBL": {
"Borough": 4,
"Block": 506,
"Lot": 29
},
"adjacentNumber": 1,
"geo": "POINT(-73.935242 40.730610)"
},
{
"BBL": {
"Borough": 4,
"Block": 506,
"Lot": 30
},
"adjacentNumber": 2,
"geo": "POINT(-73.935300 40.730650)"
}
]
}'
Response Example:
{
"status": "success",
"message": "Adjacents updated successfully for project 10890",
"adjacents_count": 2,
"adjacents": [
{
"BBL": {"Borough": 4, "Block": 506, "Lot": 29},
"bbl_string": "4005060029",
"adjacentNumber": 1,
"direction": "Southwest",
"geo": "POINT(-73.935242 40.730610)"
},
{
"BBL": {"Borough": 4, "Block": 506, "Lot": 30},
"bbl_string": "4005060030",
"adjacentNumber": 2,
"direction": "South",
"geo": "POINT(-73.935300 40.730650)"
}
]
}
Features: - Automatic Direction Calculation: Determines cardinal direction from geography coordinates - BBL Validation: Validates Borough (1-5), Block (1-99999), Lot (1-9999) - 10-Digit BBL String: Automatically generates standardized BBL string - Upsert Operation: Replaces all existing adjacents for the project - ArcGIS Integration: Looks up property data from MAPPLUTO REST API
PDF Compilation with Cross-Project Files
curl -X POST "https://your-api-url/api/compile/compile_report/10890" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"compress_pdf": false,
"files_project_number": "64"
}'
Use Case: Compile report for project 10890 but use appendix files (aerials, sanborns, database files, city directories) from project 64. Useful for: - Shared sites across multiple projects - Multi-phase environmental assessments - Historical file reuse - Site boundary changes with same location
Response:
{
"success": true,
"pdf_url": "gs://compiled_reports/project_10890/Phase_I_Report_10890.pdf",
"file_size_mb": 15.2,
"pages": 45,
"compilation_time_seconds": 67.5,
"files_source": {
"report_project": 10890,
"files_project": 64,
"cross_project_lookup": true
},
"appendices_included": {
"photos": "embedded_in_template",
"aerials": true,
"database_files": true
}
}
Generate City Directory Document
curl -X POST "https://your-api-url/api/report/generate_city_directory_document" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"act_project_number": "123",
"adjacent_north": "North Property Description",
"adjacent_south": "South Property Description",
"adjacent_east": "East Property Description",
"adjacent_west": "West Property Description"
}'
Get All Project Data
curl -X GET "https://your-api-url/api/report/get_all_project_data?ACT_Project_Number=123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Get Projects in GeoJSON Format (for Mapping)
# Get specific project with all spatial data
curl -X GET "https://your-api-url/api/map/get_projects_geojson?act_project_number=123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
# Get all projects for mapping overview
curl -X GET "https://your-api-url/api/map/get_projects_geojson" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Get Adjacent Properties for Mapping
curl -X GET "https://your-api-url/api/map/get_adjacents_geojson?act_project_number=123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Get Nearby Lots within Distance
# Get lots within 100 feet (default)
curl -X GET "https://your-api-url/api/map/get_nearby_lots_geojson?act_project_number=123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
# Get lots within custom distance (e.g., 200 feet)
curl -X GET "https://your-api-url/api/map/get_nearby_lots_geojson?act_project_number=123&distance_feet=200" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Response includes subject lot identification:
{
"status": "success",
"act_project_number": 123,
"distance_feet": 200,
"format": "geojson",
"data_source": "NYC PLUTO",
"geojson": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [...] },
"properties": {
"BBL": {"Borough": "3", "Block": 855, "Lot": 25},
"bbl_string": "3008550025",
"Address": "123 MAIN ST",
"is_subject_lot": true,
"distance_meters": 0,
"distance_feet": 0,
"OwnerName": "PROJECT OWNER LLC"
}
},
{
"type": "Feature",
"properties": {
"BBL": {"Borough": "3", "Block": 855, "Lot": 23},
"bbl_string": "3008550023",
"is_subject_lot": false,
"distance_meters": 15.2,
"distance_feet": 49.9
}
}
]
}
}
Subject Lot Logic:
- NYC Projects: Lot is marked is_subject_lot: true if its BBL matches any of the project's BBL(s)
- Non-NYC Projects: Lot is marked is_subject_lot: true if its geometry intersects with the project's geography
- Uses Geography field from ACT_Projects table (no address geocoding)
- Correctly identifies project lot even when multiple lots are in search radius
# Find lots near an address (200 feet default)
curl -X GET "https://your-api-url/api/map/get_nearby_lots_by_address?address=123%20Main%20St%2C%20Brooklyn%2C%20NY%2011201" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
# Find lots near an address with custom distance (500 feet)
curl -X GET "https://your-api-url/api/map/get_nearby_lots_by_address?address=123%20Main%20St%2C%20Albany%2C%20NY%2012207&distance_feet=500" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
Response Format for get_nearby_lots_by_address
The endpoint returns different lot identification formats depending on location:
For NYC addresses:
{
"status": "success",
"query_address": "123 Montague Street, Brooklyn, NY 11201",
"formatted_address": "123 Montague St, Brooklyn, NY 11201, USA",
"geocoded_location": {"lat": 40.6935, "lon": -73.9932},
"distance_feet": 200,
"data_source": "NYC PLUTO",
"is_nyc": true,
"borough": "Brooklyn",
"geojson": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [...] },
"properties": {
// UNIFIED FIELDS (use these for add_project)
"TaxParcels": "3002650001",
"Jurisdiction": "NYC",
// Legacy fields (still included)
"BBL": {
"Borough": "3",
"Block": 265,
"Lot": 1
},
"bbl_string": "3002650001",
"address": "123 MONTAGUE ST",
"owner_name": "EXAMPLE OWNER LLC",
"building_class": "D1",
"lot_area": 5000,
"building_area": 15000,
"num_floors": 5,
"year_built": 1920,
"zone_district": "R6",
"distance_meters": 45.2,
"distance_feet": 148.3,
"is_subject_lot": true,
"has_geometry": true,
"data_source": "NYC_PLUTO",
"is_nyc": true,
"borough": "Brooklyn"
}
}
],
"properties": {
"query_address": "123 Montague Street, Brooklyn, NY 11201",
"formatted_address": "123 Montague St, Brooklyn, NY 11201, USA",
"geocoded_location": {"lat": 40.6935, "lon": -73.9932},
"search_distance_feet": 200,
"search_distance_meters": 60.96,
"total_nearby_lots": 12,
"data_source": "NYC PLUTO",
"is_nyc": true,
"borough": "Brooklyn"
}
}
}
For non-NYC addresses:
{
"status": "success",
"query_address": "100 State St, Albany, NY 12207",
"formatted_address": "100 State St, Albany, NY 12207, USA",
"geocoded_location": {"lat": 42.6526, "lon": -73.7562},
"distance_feet": 200,
"data_source": "NYS Tax Parcels",
"is_nyc": false,
"borough": null,
"geojson": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": { "type": "Polygon", "coordinates": [...] },
"properties": {
// UNIFIED FIELDS (use these for add_project)
"TaxParcels": "65.2-1-35",
"Jurisdiction": "NYS",
// Legacy fields (still included)
"lot_number": "65.2-1-35",
"SBL": "65.2-1-35",
"SWIS": "010100",
"address": "100 STATE ST",
"street_number": "100",
"street_name": "STATE ST",
"city": "ALBANY",
"county": "ALBANY",
"zip": "12207",
"owner": "STATE OF NEW YORK",
"property_class": "612",
"land_value": 500000,
"total_value": 5000000,
"acreage": 0.5,
"distance_meters": 35.8,
"distance_feet": 117.5,
"is_subject_lot": true,
"has_geometry": true,
"data_source": "NYS_Tax_Parcels",
"is_nyc": false
}
}
],
"properties": {
"query_address": "100 State St, Albany, NY 12207",
"formatted_address": "100 State St, Albany, NY 12207, USA",
"geocoded_location": {"lat": 42.6526, "lon": -73.7562},
"search_distance_feet": 200,
"search_distance_meters": 60.96,
"total_nearby_lots": 8,
"data_source": "NYS Tax Parcels",
"is_nyc": false
}
}
}
Key Differences:
- All jurisdictions now include TaxParcels and Jurisdiction fields (unified format)
- NYC lots use BBL (Borough-Block-Lot) in legacy format
- Non-NYC lots use lot_number (SBL - Section-Block-Lot) in legacy format
- Different property attributes are available depending on the data source
β¨ Recommended Workflow:
1. Call get_nearby_lots_by_address to get nearby lots
2. Extract TaxParcels and Jurisdiction from the response
3. Pass these values to add_project endpoint
π See UNIFIED_TAXPARCELS_WORKFLOW.md for complete examples.
Inspection Management
# Schedule a new inspection
curl -X POST "https://your-api-url/api/project/schedule_inspection" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"ACT_Project_Number": "123",
"assignee_emails": ["inspector@company.com"],
"date": "2025-08-01T10:00:00",
"site_contact_name": "John Doe",
"site_contact_email": "john@example.com",
"description": "Phase I environmental inspection"
}'
# Update an existing inspection
curl -X PUT "https://your-api-url/api/project/update_inspection" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_id": "abc123def456",
"date": "2025-08-01T14:00:00",
"assignee_emails": ["newinspector@company.com"],
"description": "Updated inspection time"
}'
# Delete an inspection
curl -X DELETE "https://your-api-url/api/project/delete_inspection" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"event_id": "abc123def456",
"send_cancellation": true
}'
Get Adjacent Properties for Mapping
curl -X GET "https://your-api-url/api/map/get_adjacents_geojson?act_project_number=123" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
QuickBooks Integration
# Get QB authorization URL
curl -X GET "https://your-api-url/api/qb/qb_auth_url" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
# Get QB customers
curl -X GET "https://your-api-url/api/qb/qb_customers" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN"
# Create QB customer
curl -X POST "https://your-api-url/api/qb/qb_create_customer" \
-H "Authorization: Bearer YOUR_GOOGLE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Name": "Example Customer",
"CompanyName": "Example Corp",
"BillAddr": {
"Line1": "123 Main St",
"City": "New York",
"CountrySubDivisionCode": "NY",
"PostalCode": "10001"
},
"PrimaryEmailAddr": {
"Address": "customer@example.com"
},
"PrimaryPhone": {
"FreeFormNumber": "555-1234"
}
}'
Map API Best Practices & Common Use Cases
Choosing the Right Endpoint
Use get_nearby_lots_geojson when:
- You have an existing ACT project number
- You need to find lots around a project site
- You want to exclude the project's own lots from results
- You're doing environmental radius searches for Phase I reports
- Default distance is 500 feet (customizable)
Use get_nearby_lots_by_address when:
- You need to research a location without creating a project
- You're doing preliminary site analysis
- You want to find properties near any address (NYC or non-NYC)
- You're scouting for new business opportunities
- Default distance is 200 feet (customizable)
Use get_adjacents_geojson when:
- You only need properties that share a boundary with the project
- You're compiling adjacent property lists for reports
- You need directional information (North, South, East, West)
Distance Recommendations
| Use Case | Recommended Distance | Endpoint |
|---|---|---|
| Adjacent properties only | Use adjacents endpoint | get_adjacents_geojson |
| Immediate neighbors | 100-200 feet | get_nearby_lots_by_address |
| Phase I environmental (standard) | 200-500 feet | Either nearby endpoint |
| Neighborhood analysis | 500-1000 feet | Either nearby endpoint |
| Market research | 1000-2000 feet | Either nearby endpoint |
NYC vs Non-NYC Data Differences
NYC Properties (PLUTO Data)
- Identifier: BBL (Borough-Block-Lot)
- Borough: 1=Manhattan, 2=Bronx, 3=Brooklyn, 4=Queens, 5=Staten Island
- BBL String Format: 10 digits (e.g., "3002650001")
- Rich Building Data: Building class, number of floors, building area
- Zoning Information: Zoning districts, overlays, special districts
- Assessment Data: Less detailed than non-NYC
Non-NYC Properties (NYS Tax Parcels)
- Identifier: SBL (Section-Block-Lot) / lot_number
- Format varies by municipality (e.g., "65.2-1-35")
- SWIS Code: Statewide identification system for municipality
- Detailed Assessment: Land value, total value, full market value
- Acreage Data: Precise lot size in acres
- Municipal Info: County, city/town, school district
Example Workflows
Workflow 1: Complete Site Research from Address
# Step 1: Find nearby lots from address
curl -X GET "https://app.act.earth/api/map/get_nearby_lots_by_address?address=123%20Main%20St%2C%20Brooklyn%2C%20NY&distance_feet=300" \
-H "Authorization: Bearer $TOKEN"
# Step 2: Identify subject property BBL from results
# Step 3: Create project with that BBL
# Step 4: Get detailed adjacent properties
curl -X GET "https://app.act.earth/api/map/get_adjacents_geojson?act_project_number=12345" \
-H "Authorization: Bearer $TOKEN"
Workflow 2: Environmental Radius Search
# For existing project - use project-based search
curl -X GET "https://app.act.earth/api/map/get_nearby_lots_geojson?act_project_number=12345&distance_feet=500" \
-H "Authorization: Bearer $TOKEN"
# Returns lots within 500 feet, excluding project's own parcels
# Perfect for "within one-eighth mile" environmental requirements
Workflow 3: Market Comparison Analysis
# Find comparable properties in area
curl -X GET "https://app.act.earth/api/map/get_nearby_lots_by_address?address=350%205th%20Ave%2C%20NY&distance_feet=1000" \
-H "Authorization: Bearer $TOKEN"
# Analyze returned properties for:
# - Similar building classes
# - Comparable lot sizes
# - Same zoning districts
# - Recent development activity
Integration with Mapping Libraries
Leaflet.js Example
// Fetch and display nearby lots on map
async function displayNearbyLots(address, distanceFeet = 200) {
const response = await fetch(
`https://app.act.earth/api/map/get_nearby_lots_by_address?` +
`address=${encodeURIComponent(address)}&distance_feet=${distanceFeet}`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const data = await response.json();
// Add GeoJSON to map
L.geoJSON(data.geojson, {
style: feature => ({
color: feature.properties.is_nyc ? '#0000ff' : '#ff0000',
weight: 2,
fillOpacity: 0.3
}),
onEachFeature: (feature, layer) => {
const props = feature.properties;
const id = props.is_nyc ? props.bbl_string : props.lot_number;
layer.bindPopup(`
<strong>${id}</strong><br>
${props.address}<br>
Distance: ${props.distance_feet} ft
`);
}
}).addTo(map);
// Center map on geocoded location
map.setView([data.geocoded_location.lat, data.geocoded_location.lon], 17);
}
Mapbox GL JS Example
// Add nearby lots as a layer
map.on('load', async () => {
const response = await fetch(
'https://app.act.earth/api/map/get_nearby_lots_by_address?' +
'address=Times Square, NY&distance_feet=300',
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const data = await response.json();
map.addSource('nearby-lots', {
type: 'geojson',
data: data.geojson
});
map.addLayer({
id: 'lots-fill',
type: 'fill',
source: 'nearby-lots',
paint: {
'fill-color': [
'case',
['get', 'is_nyc'], '#4264fb',
'#42b983'
],
'fill-opacity': 0.4
}
});
map.addLayer({
id: 'lots-outline',
type: 'line',
source: 'nearby-lots',
paint: {
'line-color': '#000',
'line-width': 1
}
});
});
Performance Tips
- Use Appropriate Distance: Larger distances return more results and take longer
- Keep under 1000 feet for best response times
-
For large areas, consider multiple smaller queries
-
Cache Geocoding Results: The address geocoding step adds latency
- Cache frequently used addresses
-
Store geocoded coordinates for repeated searches
-
Filter Results Client-Side:
- Filter by property type, zoning, or other attributes after receiving data
-
More efficient than making multiple API calls
-
Batch Related Queries:
- If you need both adjacent and nearby lots, fetch in parallel
- Combine results on client side
Error Handling Best Practices
async function safelyFetchNearbyLots(address, distanceFeet = 200) {
try {
const response = await fetch(
`https://app.act.earth/api/map/get_nearby_lots_by_address?` +
`address=${encodeURIComponent(address)}&distance_feet=${distanceFeet}`,
{
headers: { 'Authorization': `Bearer ${token}` },
timeout: 30000 // 30 second timeout
}
);
if (!response.ok) {
const error = await response.json();
if (response.status === 404) {
console.error('Address not found:', error.error);
// Show user-friendly "address not found" message
return null;
} else if (response.status === 400) {
console.error('Invalid parameters:', error.error);
// Validate input and retry
return null;
} else {
throw new Error(error.error);
}
}
const data = await response.json();
if (data.geojson.properties.total_nearby_lots === 0) {
console.warn('No lots found within distance');
// Suggest increasing search radius
}
return data;
} catch (error) {
console.error('Failed to fetch nearby lots:', error);
// Implement retry logic or fallback
return null;
}
}
Authentication
All endpoints except /health require Google OAuth 2.0 authentication. Include the bearer token in the Authorization header:
Authorization: Bearer YOUR_GOOGLE_OAUTH_TOKEN
Required OAuth Scopes
Your Google OAuth token must include the following scopes for full API functionality:
BigQuery Access (Required for all data endpoints):
- https://www.googleapis.com/auth/bigquery.readonly - Read-only access to BigQuery
- https://www.googleapis.com/auth/bigquery - Full BigQuery access (recommended)
Google Cloud Platform (Recommended):
- https://www.googleapis.com/auth/cloud-platform - Full Google Cloud access
Google Calendar (Required for inspection management):
- https://www.googleapis.com/auth/calendar - Calendar access for scheduling inspections
Required IAM Permissions
Your Google account must have these IAM roles in the act-phase-i project:
- BigQuery Data Viewer - Read access to BigQuery datasets
- BigQuery Job User - Permission to run BigQuery queries
- BigQuery User - Recommended for full functionality
Token Validation
Test your token before making API calls:
curl "https://oauth2.googleapis.com/tokeninfo?access_token=YOUR_TOKEN"
Data Mapping & Field Enhancements
Building Data Integration
The API now includes comprehensive building data mapping from multiple sources:
- PLUTO Integration: Building area, frontage, depth, and dimensional data
- Google Maps API: Enhanced location details and cross street detection
- Form Data: Construction materials and interior details from form submissions
- NYC Open Data: Violations, permits, and regulatory information
- Spatial Analysis: Adjacent properties, nearby lots, and geographic boundaries
Location Field Improvements
Enhanced location data formatting provides user-friendly field population:
{
"Borough": "Brooklyn", // Full name vs "BK"
"PortionOfBorough": "Boerum Hill", // Specific neighborhood vs generic text
"CrossStreets": "Court St & State St" // Real streets vs "Street 1"
}
Field Mapping Coverage
Complete template field mapping includes:
- Building Dimensions: Area, frontage, depth with proper units
- Floor Areas: Total, above grade, below grade calculations
- Location Data: Normalized borough names and specific neighborhoods
- Construction Details: Materials from form submissions
- Cross Streets: Real street names via Google Maps geocoding
- Spatial Data: Adjacent properties, nearby lots within custom distances, GeoJSON formatting
Spatial Data & GeoJSON Features
Enhanced mapping capabilities provide comprehensive spatial analysis:
{
"geojson": {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"BBL": {"Borough": "1", "Block": "123", "Lot": "45"},
"bbl_string": "1001230045",
"Address": "123 Main St",
"distance_feet": 75.5,
"has_geometry": true
},
"geometry": {...}
}
]
}
}
Spatial Analysis Features: - Adjacent Properties: Direct property boundary neighbors with BBL and ownership data - Proximity Search: Customizable distance-based lot searches (default 100 feet, configurable) - PLUTO Integration: Complete NYC property database with building characteristics - WKT Conversion: BigQuery GEOGRAPHY fields converted to client-parseable formats - Distance Calculations: Precise measurements in both feet and meters
Database Schema
NYC Property Data Sources
ArcGIS PLUTO REST API (Primary for Property Data)
The API now uses the ArcGIS REST Feature Service for NYC PLUTO data instead of BigQuery PLUTOSHAPE:
- Service URL:
https://services5.arcgis.com/GfwWNkhOj9bNBqoJ/arcgis/rest/services/MAPPLUTO/FeatureServer/0 - Query Format: REST API with JSON responses
- Features:
- Real-time NYC property data
- Spatial queries with BBL filtering
- Property characteristics (area, frontage, depth, zoning)
- Ownership information
- Building details (class, floors, year built)
- Benefits:
- No BigQuery query costs for PLUTO data
- Simplified maintenance and updates
- Standard REST API integration
- Same field mappings as previous PLUTOSHAPE queries
Parcel Identifiers (Multi-Jurisdiction)
The API supports jurisdiction-specific parcel identifiers stored in the TaxID field:
NYC - BBL (Borough-Block-Lot)
10-Digit String Format (Preferred):
- Structure: BBBBBLLLL
- Position 0: Borough code (1=Manhattan, 2=Bronx, 3=Brooklyn, 4=Queens, 5=Staten Island)
- Positions 1-5: Tax Block (5 digits, zero-padded)
- Positions 6-9: Tax Lot (4 digits, zero-padded)
- Examples:
- "1008470040" = Manhattan (1), Block 847, Lot 40
- "4005060029" = Queens (4), Block 506, Lot 29
- "3012345678" = Brooklyn (3), Block 12345, Lot 678
- API Input: {"BBL": "1008470040"}
- Data Source: NYC MAPPLUTO
Legacy Object Format (Still Supported):
{
"BBL": [{"Borough": "MN", "Block": "847", "Lot": "40"}]
}
NYS - SBL (Section-Block-Lot)
- Format: Variable (e.g.,
"65.2-1-35") - Components: Section, Block, Lot separated by periods/hyphens
- Additional Fields: SWIS code, PRINT_KEY, County name
- API Input: Address only - SBL auto-extracted
- Data Source: NYS Tax Parcels Public API
Nassau County, NY - SBL
- Format: Variable (e.g.,
"123-456-789") - Additional Fields: PRINT_KEY, Municipality
- API Input: Address only - SBL auto-extracted
- Data Source: Nassau County Tax Parcels
New Jersey - PAMS_PIN
- Format: Alphanumeric PIN
- Additional Fields: Block, Lot, Municipality, County
- API Input: Address only - PAMS_PIN auto-extracted
- Data Source: NJ MOD IV Parcels
Florida - PARCELNO
- Format: Variable by county
- Additional Fields: PARCEL_ID, County
- API Input: Address only - PARCELNO auto-extracted
- Data Source: Florida Statewide Cadastral
Validation Rules: - NYC Borough: 1-5 only - NYC Block: 1-99999 - NYC Lot: 1-9999 - Other jurisdictions: Validated against respective APIs
Database Schema
The API integrates with these BigQuery tables:
act-phase-i.Phase_I_Report.ACT_Projects
- Main project information table
- Fields: ACT_Project_Number (INTEGER), Address (STRING), Geography (GEOGRAPHY), ClientKey (INTEGER), BBL (STRING/REPEATED RECORD), TaxID (JSON), Project_Type (STRING)
- BBL Field: Legacy NYC format - can be 10-digit string or object array
- TaxID Field: Primary parcel identifier storage - JSON array with jurisdiction-specific data
- NYC:
[{"jurisdiction": "NYC", "type": "BBL", "BBL": "1008470040", "Borough": "MN", "Block": "847", "Lot": "40", "formatted": "1008470040"}] - NYS:
[{"jurisdiction": "NYS", "type": "SBL", "SBL": "65.2-1-35", "SWIS": "123456", "County": "Albany", "formatted": "65.2-1-35"}] - Nassau:
[{"jurisdiction": "Nassau County", "type": "SBL", "SBL": "123-456-789", "PRINT_KEY": "...", "formatted": "123-456-789"}] - NJ:
[{"jurisdiction": "NJ", "type": "PAMS_PIN", "PAMS_PIN": "12345", "Municipality": "Newark", "formatted": "12345"}] - FL:
[{"jurisdiction": "Florida", "type": "PARCELNO", "PARCELNO": "12-34-56-789", "County": "Miami-Dade", "formatted": "12-34-56-789"}]
act-phase-i.Phase_I_Report.adjacents
- Adjacent properties data for mapping and analysis
- Fields: ACT_Project_Number (INTEGER), Adjacents (REPEATED RECORD)
- Adjacent Record: BBL, adjacentNumber, geo (GEOGRAPHY)
act-phase-i.Phase_I_Report.adjacents
- Adjacent properties data for mapping and spatial analysis
- Fields: ACT_Project_Number (INTEGER), Adjacents (REPEATED RECORD)
- Adjacent Record: BBL (Borough, Block, Lot), adjacentNumber (INTEGER), geo (GEOGRAPHY)
- API Support: Create/update via
/api/project/set_adjacents, retrieve via/api/map/get_adjacents_geojson
act-phase-i.Phase_I_Report.Client_List
- Client information and contact details
- Fields: ClientKey (INTEGER), ClientContact (STRING), ClientCompany (STRING), ClientAdress (STRING), ClientEmail (STRING), ClientPhone (STRING), ClientAddress (STRING)
act-phase-i.Phase_I_Report.City_Directories
- Contains Excel file links for city directory data
- Key field:
ACT_Project_Number - File types: .xlsx files
act-phase-i.Phase_I_Report.Database_Files
- Contains database file references
- Key field:
ACT_Project_Number
act-phase-i.Phase_I_Report.Sanborns
- Contains Sanborn map data
- Key field:
ACT_Project_Number
act-phase-i.Phase_I_Report.PLUTOSHAPE
- NYC PLUTO shapefile data for geography and property information
- Fields: Borough, Block, Lot, geom (GEOGRAPHY), Address, OwnerName, BldgClass, LotArea, BldgArea, NumFloors, YearBuilt, ZoneDist1
- Spatial Features: Used for proximity analysis, nearby lot searches, and spatial calculations
- Integration: Powers the get_nearby_lots_geojson endpoint with distance-based queries
Response Formats
Success Response
{
"status": "success",
"message": "Operation completed successfully",
"data": { ... },
"total_count": 10
}
Error Response
{
"error": "Error description"
}
File Structure
Report_Generation/
βββ app.py # Main Flask application with blueprint registration
βββ auth.py # Google OAuth authentication middleware
βββ config.py # Configuration settings and environment variables
βββ client_routes.py # Client management endpoints
βββ project_routes.py # Project management endpoints
βββ report_routes.py # Report generation endpoints
βββ quickbooks_routes.py # QuickBooks integration endpoints
βββ map_routes.py # Map layers and spatial data API endpoints (GeoJSON, adjacents, nearby lots)
βββ city_directory.py # City directory document generation
βββ quickbooks_utils.py # QuickBooks API utilities
βββ requirements.txt # Python dependencies (29 packages)
βββ Dockerfile # Container configuration (Python 3.11)
βββ run_flask.sh # Development server script
βββ README.md # This documentation
βββ postman/ # API testing collections
βββ Report_Generation_API_Complete_Updated.postman_collection.json
βββ Report_Generation_API_QuickBooks.postman_collection.json
βββ examples/
Development
VS Code Tasks Available
- Setup Virtual Environment: Creates Python virtual environment
- Install Requirements: Installs dependencies
- Run Flask App: Starts development server
Testing with Postman
Import the included Postman collections for comprehensive API testing:
- Report_Generation_API_Complete_Updated.postman_collection.json - Complete API collection with enhanced field mapping features (v2.3.0)
- Report_Generation_API_QuickBooks.postman_collection.json - QuickBooks-focused collection
- Environment variables: Set base_url, access_token, act_project_number, and client_key
Enhanced Field Mapping Testing: Test the new field mapping improvements by creating reports for projects with complete data: - Building dimensions will be populated from PLUTO data - Borough names will show as full names (Brooklyn, Manhattan) instead of abbreviations - Specific neighborhoods will be shown instead of generic text - Cross streets will display actual street names from Google Maps geocoding - Construction materials will be pulled from form submission data
Recent Updates
Version 4.2.0 (Current) - November 2025
- β ENHANCED SUBJECT LOT IDENTIFICATION: Improved accuracy for project-based lot searches
- Updated Endpoint:
GET /api/map/get_nearby_lots_geojson- Now includes subject lot identification - BBL-Based Matching (NYC): Uses BigQuery to compare lot BBL against project BBL(s)
- Geometry-Based Matching (Non-NYC): Uses Shapely intersection detection for NYS Tax Parcels
- Multiple Subject Lots: Supports projects with multiple BBLs (all matching lots marked as subject)
- No Geocoding: Uses Geography field directly from ACT_Projects table for accuracy
- Includes Project Lots: Subject lots now included in results (previously excluded)
- Consistent API: Both
get_nearby_lots_geojsonandget_nearby_lots_by_addressnow haveis_subject_lotflag - Use Cases: Identify correct project lot when address may geocode to adjacent parcel, multi-lot projects, boundary verification
Version 4.1.0 - November 2025
- β NEW AERIAL MAPS API: Historical aerial imagery compilation for Phase I reports
- New Endpoint:
POST /api/aerial/generate_aerial_maps- Generate historical aerial maps for projects - Dual Data Sources:
- NYC: High-resolution tile service (https://maps.nyc.gov/xyz/) at zoom 22 with 5Γ5 tile grid
- Non-NYC: NYS Orthos WMS service (https://orthos.its.ny.gov/arcgis/services/wms/)
- Comprehensive Coverage: 12-25 aerial images per project depending on location
- NYC Years: 2024, 2022, 2020, 2018, 2016, 2014, 2012, 2010, 2008, 2006, 2004, 2001-2, 1996, 1951, 1924
- NYS Years: 2000-2025 (fills gaps in NYC coverage)
- Smart Processing:
- Zoom Level 22 (highest detail) with 1280Γ1280 pixel images
- Blank image detection and filtering for NYS data
- ILoveAPI PDF merging for final compilation
- Letter-sized pages with year labels
- Storage & Tracking:
- Google Cloud Storage:
gs://act_aerials/project_{number}/ - BigQuery tracking:
Phase_I_Report.Aerialstable
- Google Cloud Storage:
- Use Cases: Phase I environmental reports, historical site analysis, temporal change detection
- β SUBJECT LOT IDENTIFICATION: Enhanced property search with subject lot marking
- New Feature:
is_subject_lotflag inget_nearby_lots_by_addressendpoint - Point-in-Polygon Detection: Uses Shapely geometry operations for accurate identification
- NYC & Non-NYC Support: Works for both PLUTO (NYC) and NYS Tax Parcels
- Smart Marking: Only one lot marked as subject (the lot containing the geocoded address)
- Integration: Also added to NYC PLUTO queries in
get_nearby_lots_by_address - Use Cases: Identify which parcel contains a specific address, property boundary verification
Version 4.0.0 - November 2025
- β NEW ADDRESS-BASED SPATIAL SEARCH: Revolutionary property discovery endpoint
- New Endpoint:
GET /api/map/get_nearby_lots_by_address- Find lots near any geocoded address - Automatic Geocoding: Converts any address to coordinates via Google Maps API
- Smart Location Detection: Automatically determines NYC vs non-NYC based on address components
- Dual Data Source: Seamlessly switches between NYC PLUTO and NYS Tax Parcels
- Universal Coverage: Works for NYC (5 boroughs) and all non-NYC New York State locations
- Default 200ft Radius: Optimized for quick property research (customizable)
- Rich Response Data:
- NYC: BBL identifiers, PLUTO property data, zoning, building details
- Non-NYC: SBL/lot numbers, SWIS codes, tax assessment data, acreage
- Both: Complete GeoJSON geometries, distance calculations, formatted addresses
- Use Cases: Property research without projects, market analysis, site scouting, preliminary assessments
- β COMPREHENSIVE MAP API DOCUMENTATION: Enhanced README with best practices
- Best Practices Section: Choosing endpoints, distance recommendations, workflow examples
- NYC vs Non-NYC Guide: Detailed comparison of data sources and identifiers
- Integration Examples: Leaflet.js and Mapbox GL JS code samples
- Performance Tips: Caching strategies, batch queries, optimal distance settings
- Error Handling: Production-ready error handling patterns
- β POSTMAN COLLECTION v4.0.0: Complete spatial API coverage
- New Map & Spatial Data Section: 7 comprehensive endpoints with examples
- Example Requests: Pre-configured NYC and non-NYC address examples
- Environment Variables: Test addresses for quick validation
- Detailed Descriptions: Every endpoint documented with parameters, returns, and use cases
- β ENHANCED DOCUMENTATION SUITE:
NEARBY_LOTS_BY_ADDRESS_API.md: Complete implementation documentationNEARBY_LOTS_BY_ADDRESS_QUICK_REF.md: Developer quick reference with code examplestest_nearby_lots_by_address.py: Comprehensive test script with multiple scenarios- Response format examples for both NYC and non-NYC addresses
- Table of Contents for easy navigation
Version 2.7.0
- β ARCGIS PLUTO REST API MIGRATION: Complete replacement of BigQuery PLUTOSHAPE queries
- New Data Source: ArcGIS REST Feature Service at
https://services5.arcgis.com/GfwWNkhOj9bNBqoJ/arcgis/rest/services/MAPPLUTO/FeatureServer/0 - Benefits: Real-time NYC property data, no BigQuery costs for PLUTO queries, simplified maintenance
- Features: Spatial queries, property characteristics, ownership data, building details
- Integration: Seamless replacement with same field mappings and response formats
- β ENHANCED ADJACENT PROPERTIES API: Comprehensive set_adjacents endpoint with validation
- BBL Validation: Strict Borough (1-5), Block (1-99999), Lot (1-9999) validation
- Direction Calculation: Automatic cardinal direction from geography (North, South, East, West, Northwest, etc.)
- 10-Digit BBL Format: Standardized format (1 borough + 5 block + 4 lot, e.g., 4005060029)
- Upsert Operations: Complete replacement of existing adjacents for data integrity
- β CROSS-PROJECT FILE LOOKUP: files_project_number parameter for report compilation
- Feature: Pull appendix files from different ACT project numbers
- Use Cases: Shared sites, multi-phase projects, historical file reuse
- Endpoints: Available in create_project_report and compile_report
- β PAD FILE PATH RESOLUTION: Enhanced Property Address Directory file handling
- Robust Path Search: Checks multiple locations (relative, /app, file directory, cwd)
- Validation: Path existence verification before file operations
- Error Handling: Detailed logging for troubleshooting Cloud Run deployments
- Cloud Run Ready: Optimized for containerized environments
Version 2.6.0
- β SITE DIAGRAM API FOR iOS APPS: Complete two-endpoint architecture for mobile applications
- New Endpoint:
POST /api/project/generate_site_diagram_with_elevation- Generate and store all core data types - New Endpoint:
GET /api/project/get_site_diagram- Retrieve stored site diagram data for iOS consumption - Real NYC DEM Data: Uses NYC DEM 2017 1-foot LiDAR service for accurate elevation processing
- Enhanced BigQuery Schema: Added
Elevation_metadataJSON column for detailed elevation band storage - 12 Color-Coded Elevation Bands: Topographic gradient with detailed pixel counts and statistics
- GCS Storage Integration: Elevation raster images stored with public URLs for mobile access
- iOS-Optimized Format: Single API response with all GeoJSON layers and elevation metadata
- Cross-Project Compatibility: Verified working across different ACT project numbers and locations
- β ELEVATION PROCESSING ENHANCEMENTS: Advanced elevation data handling
- PIL Color Coding: Real-time elevation band generation with color mapping
- Precision Handling: JSON-safe floating point precision for BigQuery storage
- Coordinate Transformations: EPSG:4326 to EPSG:2263 conversion for NYC DEM integration
- Statistical Analysis: Elevation min/max/range calculations with band distribution
- Metadata Separation: Dedicated column for elevation bands separate from main metadata
Version 2.5.0
- β ADVANCED PDF COMPILATION SYSTEM: Complete Adobe PDF Services integration with production-ready compilation
- Bearer Token Authentication: Full production API support with proper token handling
- Smart Defaults: Include all appendices by default with no compression for optimal quality
- Adobe PDF Services: Primary processor for corruption-free, high-quality PDF generation
- Insertion-Based Appendix Processing: Intelligent content insertion into existing appendix sections
- Enhanced Photo Processing: Portrait rotation, EXIF correction, and quality optimization
- β PORTRAIT PHOTO ROTATION: Advanced image processing during Word document creation
- Automatic Detection: Identifies portrait-oriented photos (height > width)
- Landscape Conversion: Rotates portrait photos 90Β° counterclockwise for optimal document layout
- EXIF Handling: Proper orientation correction from camera metadata using ImageOps.exif_transpose
- Quality Preservation: Maintains image quality during rotation and processing
- β PRODUCTION API CONFIGURATION: Enhanced production environment support
- Dynamic URL Resolution: Production photo API uses same host as main compilation service
- Token Propagation: Bearer tokens properly passed between compilation and photo generation APIs
- Fallback Systems: Graceful degradation to direct GCS bucket access if photo API unavailable
- β SMART FILTERING AND DEDUPLICATION: Intelligent content management
- PDF File Preference: Prioritizes PDF files over Excel files for database appendices
- Duplicate Prevention: Unified handling of Sanborn maps and Fire Insurance maps as same file type
- Recency Filtering: Automatically selects most recent files based on year extraction from filenames
- Size Management: Removed unnecessary compression while maintaining reasonable file sizes
- β COMPREHENSIVE ERROR HANDLING: Robust production-ready error management
- Adobe Service Fallbacks: PyPDF2 backup system for Adobe PDF Services failures
- Photo Processing Fallbacks: Direct bucket access when photo generation API unavailable
- Graceful Degradation: Continues compilation even with missing appendix files
- Detailed Logging: Enhanced debug output for troubleshooting and monitoring
Version 2.4.0
- β TWO-STEP REPORT GENERATION WORKFLOW: Major API restructuring for enhanced user control
- New Endpoint:
POST /api/report/get_report_variables- Fetch and map variables without creating report - New Endpoint:
POST /api/report/create_report_from_variables- Generate reports from reviewed variables - Enhanced User Experience: Review, edit, and validate all template variables before report generation
- Data Quality Control: Identify blank fields, validate mappings, support manual overrides
- Backward Compatibility: Legacy
/create_project_reportendpoint maintained with deprecation notice - City Directory Integration: Automatic inclusion as Section 5.5 in all reports (no separate endpoint needed)
- β COMPREHENSIVE VARIABLE MAPPING: Complete field coverage and data source transparency
- All 107 template fields mapped with source attribution
- Blank field identification for user completion
- Form data integration with comprehensive coverage analysis
- Real-time data validation and integrity checking
- β DOCUMENTATION UPDATES: Enhanced README with two-step workflow examples and updated Postman collections
Version 2.3.1
- β BIGQUERY TYPE ALIGNMENT FIX: Resolved critical STRUCT parameter type mismatch in spatial queries
- Root Cause: Fixed incompatible data types between BigQuery parameters and table schemas
- PLUTOSHAPE Integration: Aligned STRUCT parameters to match actual table schema (Borough: STRING, Block: INT64, Lot: INT64)
- Spatial Query Stability: Eliminated "Invalid value for type: STRUCT" errors in
get_nearby_lots_geojson - Data Type Conversion: Proper Borough string conversion and Block/Lot integer casting
- Production Ready: All spatial proximity queries now execute successfully without type conflicts
- β DOCUMENTATION UPDATES: Enhanced README with troubleshooting guides and technical implementation details
Version 2.3.0
- β FIELD MAPPING ENHANCEMENTS: Complete resolution of missing field mappings
- Fixed BuildingArea, BuildingFrontage, BuildingDepth mapping from PLUTO data
- Added comprehensive floor area calculations (Total, Above Grade, Below Grade)
- Enhanced building dimension data integration with proper unit conversions
- β LOCATION DATA IMPROVEMENTS: Advanced location field formatting
- Borough normalization: Converts codes (BK) to full names (Brooklyn)
- Specific neighborhood portions: Shows actual areas (Boerum Hill) vs generic text
- Real cross street detection: Google Maps API integration for accurate cross streets
- Enhanced address geocoding and validation
- β CONSTRUCTION MATERIAL MAPPING: Corrected field mappings
- CityConstruction field now properly maps to ceiling construction materials from form drafts
- Enhanced interior construction detail capture from submitted forms
- β ADJACENTS DATA MANAGEMENT: New endpoint for managing adjacent properties
POST /api/project/set_adjacents- Create/update adjacent properties data- Supports BBL validation, geography via WKT format, upsert operations
- Integrated with existing
/api/map/get_adjacents_geojsonfor data retrieval - β API RELIABILITY: Improved data source integration
- Enhanced Google Maps API integration for location details and cross streets
- Robust ArcGIS PLUTO API integration for building data
- Multi-source data validation and fallback mechanisms
- β TESTING & VALIDATION: Comprehensive field mapping verification
- 100% success rate on all location field fixes validated with live API data
- Complete testing suite for building dimension and area calculations
Version 2.2.0
- β
SPATIAL ANALYSIS API: Complete Map & Spatial Data API (
/api/map) with advanced GeoJSON endpoints - Adjacent Properties: Direct boundary neighbors with BBL and ownership data
- Proximity Search: Customizable distance-based lot searches with PLUTO integration
- Project Mapping: Individual and bulk project GeoJSON export with geography data
- Spatial Calculations: Distance measurements, WKT geometry conversion, error handling
- β ENHANCED MAPPING: Get projects data in GeoJSON format with BBL arrays and geography
- β Enhanced building class descriptions with specific NYC Building Class codes
- β Multi-lot address aggregation in AgencyAddress field
- β Comprehensive Postman collection with all 28+ endpoints including spatial analysis
- β Updated README with complete endpoint documentation and spatial data examples
Version 2.1.0
- β Added BigQuery data access endpoints
- β Enhanced city directory generation with ACT project number lookup
- β Python 3.11 upgrade with modern package versions
- β Resolved Cloud Run deployment issues
- β Improved error handling and response formats
Version 2.0.0
- β Modularized Flask application (98.8% code reduction)
- β Google Cloud Secret Manager integration
- β QuickBooks Online OAuth 2.0 flow
- β BigQuery integration for project data
- β Automated token refresh mechanisms
Support
For issues or questions: 1. Check the API documentation in this README 2. Review Postman collection examples 3. Verify authentication tokens are valid 4. Ensure BigQuery permissions are configured
Common Issues & Troubleshooting
403 Forbidden Error on Map Endpoints
If you receive a 403 Forbidden error on /api/map/get_projects_geojson or other map endpoints:
1. Token Issues:
# Test your token validity
curl "https://oauth2.googleapis.com/tokeninfo?access_token=YOUR_TOKEN"
2. Required OAuth Scopes:
Your Google OAuth token must include these scopes:
- https://www.googleapis.com/auth/bigquery.readonly (minimum)
- https://www.googleapis.com/auth/bigquery (full access)
- https://www.googleapis.com/auth/cloud-platform (recommended)
3. BigQuery Permissions:
Ensure your Google account has these IAM roles in the act-phase-i project:
- BigQuery Data Viewer (minimum for read operations)
- BigQuery Job User (required to run queries)
- BigQuery User (recommended)
4. Debug Token Information:
# Check token scopes and validity
curl -H "Authorization: Bearer YOUR_TOKEN" \
"https://your-api-url/auth_test"
5. Project Access:
Verify you have access to the act-phase-i BigQuery project and the following datasets:
- act-phase-i.Phase_I_Report.ACT_Projects
- act-phase-i.Phase_I_Report.adjacents
- act-phase-i.Phase_I_Report.PLUTOSHAPE
BigQuery STRUCT Type Mismatch (RESOLVED in v2.3.1)
Issue: "Invalid value for type: STRUCT
Root Cause: Parameter data types didn't match table schema types in spatial queries.
Resolution Applied: - Updated STRUCT parameters to match PLUTOSHAPE table schema exactly - Borough: STRING, Block: INT64, Lot: INT64 (not all strings) - Proper data type conversion in parameter preparation - Eliminated CAST operations in SQL queries for direct type matching
Prevention: This issue has been permanently resolved through proper schema alignment. All spatial proximity queries now execute successfully.
Authentication Best Practices
- Use Fresh Tokens: OAuth access tokens typically expire after 1 hour
- Include Required Scopes: When requesting OAuth tokens, include BigQuery scopes
- Test with curl: Verify endpoints work with curl before using in applications
- Check Project Permissions: Ensure your Google account has BigQuery access to the
act-phase-iproject
License
[Your License Here]