Urban Planning: Properties Near Transit
Find all residential parcels within walking distance of subway stations for transit-oriented development analysis.
Scenario Overviewβ
Goal: Identify properties within 500 meters of subway stations to assess transit-oriented development opportunities.
Real-World Application:
- Urban planning departments evaluating development zones
- Real estate developers finding transit-accessible properties
- Policy makers assessing transit equity and coverage
- Environmental planners reducing car dependency
Estimated Time: 10 minutes
Difficulty: ββ Intermediate
Prerequisitesβ
Required Dataβ
-
Parcels Layer (polygons)
- Residential property boundaries
- Must include land use or zoning attributes
- Recommended: 1,000+ features for realistic analysis
-
Transit Stations Layer (points)
- Subway/metro station locations
- Includes station names
- Covers your study area
Sample Data Sourcesβ
Option 1: OpenStreetMap (Free)
# Use QGIS QuickOSM plugin
1. Vector β QuickOSM β Quick Query
2. Key: "railway", Value: "station"
3. Select your city/region
4. Download points
Option 2: Municipal Open Data
- Check your city's open data portal
- Look for "parcels", "cadastre", or "property" datasets
- Transit data usually under "transportation"
System Requirementsβ
- Recommended Backend: PostgreSQL (for 50k+ parcels)
- Alternative: Spatialite (for
<50kparcels) - CRS: Any (FilterMate handles reprojection automatically)
Step-by-Step Instructionsβ
Step 1: Load Your Dataβ
- Open QGIS and create a new project
- Load the parcels layer (drag & drop or Layer β Add Layer)
- Load the transit_stations layer
- Verify both layers display correctly on the map
Different CRS? No problem! FilterMate automatically reprojects layers during spatial operations. You'll see a π indicator when reprojection occurs.
Step 2: Open FilterMateβ
- Click the FilterMate icon in the toolbar
- Or: Vector β FilterMate
- The panel docks on the right side
What you should see:
- Three tabs: FILTERING / EXPLORING / EXPORTING
- Layer selector at the top
- Empty expression builder
Step 3: Configure the Filterβ
3.1 Select Target Layerβ
- In the Layer Selection dropdown (top of panel)
- Check parcels layer
- Notice the backend indicator (PostgreSQLβ‘ / Spatialite / OGR)
Layer Info Display:
Provider: postgresql (PostgreSQL)
Features: 125,347
CRS: EPSG:2154 (Lambert 93)
Primary Key: gid
If you see "OGR" for large parcel datasets, consider migrating to PostgreSQL for 10-50Γ faster performance. See Backend Guide.
3.2 Add Attribute Filter (Optional)β
Filter to residential parcels only:
- In the Expression Builder section
- Click the Fields dropdown to see available attributes
- Enter this expression:
land_use = 'residential'
-- OR if using zoning codes:
zoning LIKE 'R-%'
-- OR multiple residential types:
land_use IN ('residential', 'mixed-use', 'multi-family')
- Wait for the green checkmark (β) - indicates valid syntax
Expression Explanation:
land_use = 'residential'- Exact match on land use fieldLIKE 'R-%'- Pattern matching for residential zoning codes (R-1, R-2, etc.)IN (...)- Multiple allowed values
If your data doesn't have land use, skip this step. The spatial filter will work on all parcels.
3.3 Configure Geometric Filterβ
Now add the spatial component - proximity to transit:
- Scroll down to the Geometric Filter section
- Click to expand if collapsed
Reference Layer: 3. Select transit_stations from the dropdown 4. The reference layer icon appears: π
Spatial Predicate: 5. Select "Intersects" from predicate dropdown
- (We'll add buffer distance, so intersects = "touches buffer")
Buffer Distance:
6. Enter 500 in the distance field
7. Select meters as the unit
8. Leave buffer type as Round (Planar) for urban areas
Your Configuration Should Look Like:
Reference Layer: transit_stations
Spatial Predicate: Intersects
Buffer Distance: 500 meters
Buffer Type: Round (Planar)
If your layers use geographic coordinates (EPSG:4326), FilterMate automatically converts to EPSG:3857 for accurate metric buffers. You'll see: π indicator in logs.
Step 4: Apply the Filterβ
- Click the Apply Filter button (big button at bottom)
- FilterMate executes the spatial query
What Happens:
- PostgreSQL Backend
- Spatialite Backend
- OGR Backend
-- Creates optimized materialized view
CREATE MATERIALIZED VIEW temp_filter AS
SELECT p.*
FROM parcels p
WHERE p.land_use = 'residential'
AND EXISTS (
SELECT 1 FROM transit_stations s
WHERE ST_DWithin(
p.geom::geography,
s.geom::geography,
500
)
);
CREATE INDEX idx_temp_geom
ON temp_filter USING GIST(geom);
β‘ Performance: 0.3-2 seconds for 100k+ parcels
-- Creates temporary table with spatial index
CREATE TEMP TABLE temp_filter AS
SELECT p.*
FROM parcels p
WHERE p.land_use = 'residential'
AND EXISTS (
SELECT 1 FROM transit_stations s
WHERE ST_Distance(p.geom, s.geom) <= 500
);
SELECT CreateSpatialIndex('temp_filter', 'geom');
β±οΈ Performance: 5-15 seconds for 50k parcels
Uses QGIS Processing framework with memory layers.
π Performance: 30-120 seconds for large datasets
Recommendation: Migrate to PostgreSQL for this workflow.
Step 5: Review Resultsβ
Map View:
- Filtered parcels are highlighted on the map
- Non-matching parcels are hidden (or greyed out)
- Count displayed in FilterMate panel:
Found: 3,247 features
Verify Results:
- Zoom to a transit station
- Select one filtered parcel
- Use Measure Tool to verify it's within 500m of station
Expected Results:
- Urban cores: High density of filtered parcels
- Suburban areas: Sparse parcels near stations
- Rural areas: Very few or no results
Step 6: Analyze & Exportβ
Option A: Quick Statisticsβ
- Right-click filtered layer
- Properties β Information
- View feature count and extent
Option B: Export for Reportingβ
-
Switch to EXPORTING tab in FilterMate
-
Select filtered parcels layer
-
Choose output format:
- GeoPackage (.gpkg) - Best for QGIS
- GeoJSON - For web mapping
- Shapefile - For legacy systems
- PostGIS - Back to database
-
Optional: Transform CRS (e.g., WGS84 for web)
-
Click Export
Export Settings Example:
Layer: parcels (filtered)
Format: GeoPackage
Output CRS: EPSG:4326 (WGS84)
Filename: transit_accessible_parcels.gpkg
Understanding the Resultsβ
Interpreting Feature Countsβ
Example Results:
Total parcels: 125,347
Residential parcels: 87,420 (70%)
Transit-accessible residential: 3,247 (3.7% of residential)
What This Means:
- Only 3.7% of residential parcels are transit-accessible
- Opportunity for transit-oriented development
- Most residents depend on cars (equity concern)
Spatial Patternsβ
Look for:
- Clusters around major transit hubs β High-density zones
- Gaps between stations β Potential infill development
- Isolated parcels β Transit deserts requiring service expansion
Best Practicesβ
Performance Optimizationβ
β Use PostgreSQL for parcel datasets >50k` features
- 10-50Γ faster than OGR backend
- Sub-second query times even on 500k+ parcels
β Filter by attribute first if possible
land_use = 'residential'reduces spatial query scope- 30-50% performance improvement
β Buffer Distance Units
- Use meters for urban analysis (consistent worldwide)
- Avoid degrees for distance-based queries (inaccurate)
Accuracy Considerationsβ
β οΈ Buffer Type Selection:
- Round (Planar): Fast, accurate for small areas (
<10km) - Round (Geodesic): More accurate for large regions
- Square: Computational optimization (rarely needed)
β οΈ CRS Choice:
- Local projected CRS (e.g., State Plane, UTM) - Best accuracy
- Web Mercator (EPSG:3857) - Good for worldwide analysis
- WGS84 (EPSG:4326) - Auto-converted by FilterMate β
Data Qualityβ
π Check for:
- Overlapping parcels - Can inflate counts
- Missing geometries - Use "Check Geometries" tool
- Outdated transit data - Verify station operational status
Common Issues & Solutionsβ
Issue 1: No Results Foundβ
Symptoms: Filter returns 0 features, but you expect matches.
Possible Causes:
- β Buffer distance too small (try 1000m)
- β Wrong attribute value (check
land_usefield values) - β Layers don't overlap geographically
- β CRS mismatch (though FilterMate handles this)
Debug Steps:
-- Test 1: Remove attribute filter
-- Just run spatial query on all parcels
-- Test 2: Increase buffer distance
-- Try 1000 or 2000 meters
-- Test 3: Reverse query
-- Filter stations within parcels (should always return results)
Issue 2: Slow Performance (>30` seconds)β
Cause: Large dataset with OGR backend.
Solutions:
- β Install PostgreSQL + PostGIS
- β Load data into PostgreSQL database
- β Use PostgreSQL layer in QGIS
- β Re-run filter (expect 10-50Γ speedup)
Quick PostgreSQL Setup:
# Install psycopg2 for QGIS Python
pip install psycopg2-binary
# Or in OSGeo4W Shell (Windows):
py3_env
pip install psycopg2-binary
Issue 3: Results Look Wrongβ
Symptoms: Parcels far from stations are included.
Possible Causes:
- β Buffer distance in wrong units (degrees instead of meters)
- β "Contains" predicate instead of "Intersects"
- β Reference layer is wrong (roads instead of stations)
Verification:
- Use QGIS Measure Tool
- Measure distance from filtered parcel to nearest station
- Should be β€ 500 meters
Next Stepsβ
Related Workflowsβ
- Emergency Services Coverage - Similar distance analysis
- Environmental Protection Zones - Multi-criteria filtering
- Real Estate Analysis - Combined attribute filtering
Advanced Techniquesβ
Graduated Buffers: Run multiple filters with different distances (250m, 500m, 1000m) to create walkability zones.
Combine with Demographics: Join census data to estimate transit-accessible population.
Time-Based Analysis: Use historical data to track transit-oriented development over time.
Summaryβ
You've Learned:
- β Combined attribute and geometric filtering
- β Buffer operations with distance parameters
- β Spatial predicate selection (Intersects)
- β Backend performance optimization
- β Result export and CRS transformation
Key Takeaways:
- FilterMate handles CRS reprojection automatically
- PostgreSQL backend provides best performance for large datasets
- 500m is typical "walking distance" for urban planning
- Always verify results with manual measurement sampling
Time Saved:
- Manual selection: ~2 hours
- Processing Toolbox (multi-step): ~20 minutes
- FilterMate workflow: ~10 minutes β‘
Need help? Check the Troubleshooting Guide or ask on GitHub Discussions.