Feature Querying with WFS
Scenario: You need to retrieve raw geographic features from a Qarta layer for analysis, export, or downstream processing. You want to filter by location (bounding box) or attributes (CQL filter), and retrieve the data as GeoJSON.
Workflow:
- Use
DescribeLayerto find the WFStypeNamesfor a layer - Call
GetFeaturewith filters and output format - Parse GeoJSON and process features in your application
- Paginate through large result sets if needed
Step 1: Get Layer Metadata
Use the WMS DescribeLayer operation to find the corresponding WFS layer name:
- cURL
curl -X GET "https://graph.quarticle.ro/graph/layers/wms/DescribeLayer" \
-H "Authorization: YOUR_API_KEY" \
-G \
-d "service=WMS" \
-d "version=1.1.1" \
-d "request=DescribeLayer" \
-d "layers=GRAPHRASTER:cities"
From the response, extract the WFS typeNames (usually same as the layer name).
Step 2: Query Features by Bounding Box
Retrieve all features within a geographic area:
- cURL
- JavaScript
- Python
# Cities in New York area (bbox in EPSG:4326)
curl -X GET "https://graph.quarticle.ro/graph/layers/wfs/GetFeature" \
-H "Authorization: YOUR_API_KEY" \
-G \
-d "service=WFS" \
-d "version=2.0.0" \
-d "request=GetFeature" \
-d "typeNames=GRAPHRASTER:cities" \
-d "bbox=-74.1,40.6,-73.9,40.8,EPSG:4326" \
-d "outputFormat=application/json" \
| jq '.features[] | .properties'
async function getCitiesByBbox() {
const apiKey = process.env.QARTA_API_KEY;
const params = new URLSearchParams({
service: 'WFS',
version: '2.0.0',
request: 'GetFeature',
typeNames: 'GRAPHRASTER:cities',
bbox: '-74.1,40.6,-73.9,40.8,EPSG:4326',
outputFormat: 'application/json'
});
const response = await fetch(
`https://graph.quarticle.ro/graph/layers/wfs/GetFeature?${params}`,
{ headers: { 'Authorization': `${apiKey}` } }
);
const geojson = await response.json();
console.log(`Found ${geojson.features.length} cities in the area`);
return geojson.features;
}
const cities = await getCitiesByBbox();
cities.forEach(city => {
console.log(`${city.properties.name}: ${city.properties.population}`);
});
import requests
api_key = os.getenv('QARTA_API_KEY')
response = requests.get(
'https://graph.quarticle.ro/graph/layers/wfs/GetFeature',
params={
'service': 'WFS',
'version': '2.0.0',
'request': 'GetFeature',
'typeNames': 'GRAPHRASTER:cities',
'bbox': '-74.1,40.6,-73.9,40.8,EPSG:4326',
'outputFormat': 'application/json'
},
headers={'Authorization': f'{api_key}'}
)
geojson = response.json()
for feature in geojson['features']:
print(f"{feature['properties']['name']}: {feature['properties']['population']}")
Step 3: Query Features by Attributes (CQL Filter)
Filter features using attribute conditions:
- Simple Attribute Filter
- Combined Filters
- cURL Example
- JavaScript with CQL
# Cities with population > 1 million
cql_filter=population > 1000000
# Urban areas in the USA with population > 500k
cql_filter=type = 'urban' AND country = 'USA' AND population > 500000
curl -X GET "https://graph.quarticle.ro/graph/layers/wfs/GetFeature" \
-H "Authorization: YOUR_API_KEY" \
-G \
-d "service=WFS" \
-d "version=2.0.0" \
-d "request=GetFeature" \
-d "typeNames=GRAPHRASTER:cities" \
-d "cql_filter=population > 1000000 AND country = 'USA'" \
-d "outputFormat=application/json"
async function getMajorUSCities() {
const apiKey = process.env.QARTA_API_KEY;
const params = new URLSearchParams({
service: 'WFS',
version: '2.0.0',
request: 'GetFeature',
typeNames: 'GRAPHRASTER:cities',
cql_filter: "population > 1000000 AND country = 'USA'",
sortBy: 'population D', // Sort by population descending
outputFormat: 'application/json'
});
const response = await fetch(
`https://graph.quarticle.ro/graph/layers/wfs/GetFeature?${params}`,
{ headers: { 'Authorization': `${apiKey}` } }
);
const geojson = await response.json();
return geojson.features;
}
const cities = await getMajorUSCities();
console.log(`Found ${cities.length} major US cities`);
Step 4: Combine Spatial and Attribute Filters
Filter by both location and attributes:
- cURL
- JavaScript
curl -X GET "https://graph.quarticle.ro/graph/layers/wfs/GetFeature" \
-H "Authorization: YOUR_API_KEY" \
-G \
-d "service=WFS" \
-d "version=2.0.0" \
-d "request=GetFeature" \
-d "typeNames=GRAPHRASTER:cities" \
-d "bbox=-74.1,40.6,-73.9,40.8,EPSG:4326" \
-d "cql_filter=population > 100000" \
-d "propertyName=name,population,geometry" \
-d "outputFormat=application/json"
async function getLargeCitiesInArea() {
const apiKey = process.env.QARTA_API_KEY;
// Query: NYC area, cities with population > 100k
const params = new URLSearchParams({
service: 'WFS',
version: '2.0.0',
request: 'GetFeature',
typeNames: 'GRAPHRASTER:cities',
bbox: '-74.1,40.6,-73.9,40.8,EPSG:4326',
cql_filter: 'population > 100000',
propertyName: 'name,population,country', // Only these fields
outputFormat: 'application/json'
});
const response = await fetch(
`https://graph.quarticle.ro/graph/layers/wfs/GetFeature?${params}`,
{ headers: { 'Authorization': `${apiKey}` } }
);
return await response.json();
}
Step 5: Paginate Through Large Result Sets
For large datasets, paginate to avoid timeouts:
- JavaScript
- Python
async function getAllFeatures(typeNames, cqlFilter = null) {
const apiKey = process.env.QARTA_API_KEY;
const pageSize = 100;
let allFeatures = [];
let page = 0;
while (true) {
const params = new URLSearchParams({
service: 'WFS',
version: '2.0.0',
request: 'GetFeature',
typeNames: typeNames,
count: pageSize.toString(),
startIndex: (page * pageSize).toString(),
outputFormat: 'application/json'
});
if (cqlFilter) {
params.append('cql_filter', cqlFilter);
}
const response = await fetch(
`https://graph.quarticle.ro/graph/layers/wfs/GetFeature?${params}`,
{ headers: { 'Authorization': `${apiKey}` } }
);
const geojson = await response.json();
const count = geojson.features.length;
allFeatures.push(...geojson.features);
console.log(`Page ${page + 1}: Retrieved ${count} features (total: ${allFeatures.length})`);
// Stop if fewer features than page size
if (count < pageSize) break;
page++;
}
return allFeatures;
}
const allCities = await getAllFeatures('GRAPHRASTER:cities', "population > 50000");
console.log(`Total: ${allCities.length} cities`);
import requests
def get_all_features(type_names, cql_filter=None):
api_key = os.getenv('QARTA_API_KEY')
page_size = 100
all_features = []
page = 0
while True:
params = {
'service': 'WFS',
'version': '2.0.0',
'request': 'GetFeature',
'typeNames': type_names,
'count': page_size,
'startIndex': page * page_size,
'outputFormat': 'application/json'
}
if cql_filter:
params['cql_filter'] = cql_filter
response = requests.get(
'https://graph.quarticle.ro/graph/layers/wfs/GetFeature',
params=params,
headers={'Authorization': f'{api_key}'}
)
geojson = response.json()
count = len(geojson['features'])
all_features.extend(geojson['features'])
print(f'Page {page + 1}: Retrieved {count} features (total: {len(all_features)})')
if count < page_size:
break
page += 1
return all_features
cities = get_all_features('GRAPHRASTER:cities', "population > 50000")
print(f'Total: {len(cities)} cities')
Step 6: Export to CSV or File
Save queried features to a file:
- JavaScript — Export to JSON
- Python — Export to CSV
async function exportFeaturesToJSON(typeNames, filename) {
const features = await getAllFeatures(typeNames);
const geojson = {
type: 'FeatureCollection',
features: features
};
const json = JSON.stringify(geojson, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
}
exportFeaturesToJSON('GRAPHRASTER:cities', 'cities.geojson');
import csv
import json
def export_features_to_csv(type_names, filename):
features = get_all_features(type_names)
if not features:
print('No features to export')
return
# Extract all property keys
all_keys = set()
for feature in features:
all_keys.update(feature['properties'].keys())
all_keys = sorted(list(all_keys))
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['geometry'] + all_keys)
writer.writeheader()
for feature in features:
row = {
'geometry': json.dumps(feature['geometry']),
**feature['properties']
}
writer.writerow(row)
print(f'Exported {len(features)} features to {filename}')
export_features_to_csv('GRAPHRASTER:cities', 'cities.csv')
Step 7: Spatial Queries (Advanced)
Use CQL spatial functions to query features:
- Features Within Distance
- Features Containing a Point
- Features Intersecting Area
# Cities within 10km of Times Square
cql_filter=DWITHIN(geometry, POINT(-73.9857 40.7580), 10000, meters)
# Districts containing a point
cql_filter=CONTAINS(geometry, POINT(-73.9857 40.7580))
# Cities intersecting a polygon
cql_filter=INTERSECTS(geometry, POLYGON((-74.0 40.7, -73.9 40.7, -73.9 40.8, -74.0 40.8, -74.0 40.7)))
Complete Workflow Example
- JavaScript
- Python
const API_KEY = process.env.QARTA_API_KEY;
async function analyzeMetropolitanAreas() {
console.log('Querying metropolitan areas...');
// Step 1: Get all cities
const params = new URLSearchParams({
service: 'WFS',
version: '2.0.0',
request: 'GetFeature',
typeNames: 'GRAPHRASTER:cities',
cql_filter: "type = 'metropolitan' AND population > 1000000",
propertyName: 'name,population,area,country',
sortBy: 'population D',
outputFormat: 'application/json'
});
const response = await fetch(
`https://graph.quarticle.ro/graph/layers/wfs/GetFeature?${params}`,
{ headers: { 'Authorization': `${API_KEY}` } }
);
const geojson = await response.json();
// Step 2: Analyze
const analysis = {
totalCities: geojson.features.length,
totalPopulation: 0,
averagePopulation: 0,
cities: []
};
geojson.features.forEach(feature => {
const { name, population, area, country } = feature.properties;
analysis.totalPopulation += population;
analysis.cities.push({
name,
population,
area,
country,
density: (population / area).toFixed(0)
});
});
analysis.averagePopulation = Math.round(analysis.totalPopulation / analysis.totalCities);
// Step 3: Export
const json = JSON.stringify(analysis, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = 'metropolitan_analysis.json';
link.click();
console.log('Analysis complete:', analysis);
return analysis;
}
analyzeMetropolitanAreas();
import requests
import json
import statistics
API_KEY = os.getenv('QARTA_API_KEY')
def analyze_metropolitan_areas():
print('Querying metropolitan areas...')
params = {
'service': 'WFS',
'version': '2.0.0',
'request': 'GetFeature',
'typeNames': 'GRAPHRASTER:cities',
'cql_filter': "type = 'metropolitan' AND population > 1000000",
'propertyName': 'name,population,area,country',
'sortBy': 'population D',
'outputFormat': 'application/json'
}
response = requests.get(
'https://graph.quarticle.ro/graph/layers/wfs/GetFeature',
params=params,
headers={'Authorization': f'{API_KEY}'}
)
geojson = response.json()
# Analyze
populations = []
areas = []
cities_data = []
for feature in geojson['features']:
props = feature['properties']
population = props['population']
area = props['area']
density = population / area if area > 0 else 0
populations.append(population)
areas.append(area)
cities_data.append({
'name': props['name'],
'population': population,
'area': area,
'country': props['country'],
'density': round(density, 0)
})
analysis = {
'totalCities': len(cities_data),
'totalPopulation': sum(populations),
'averagePopulation': round(statistics.mean(populations)),
'medianPopulation': round(statistics.median(populations)),
'cities': cities_data
}
# Export
with open('metropolitan_analysis.json', 'w') as f:
json.dump(analysis, f, indent=2)
print(f'Analysis complete: {analysis["totalCities"]} cities analyzed')
return analysis
analyze_metropolitan_areas()
Use Cases
- Real estate analysis: Query properties in specific areas and price ranges
- Environmental data: Download pollution, climate, or vegetation data for analysis
- Urban planning: Extract zoning, infrastructure, or demographic data
- Research: Bulk-download geographic features for spatial analysis
- Data pipeline: Integrate WFS queries into ETL processes
Next Steps
- WFS Guide — Learn all GetFeature parameters and CQL syntax
- Map Visualization Use Case — Display features on maps
- GeoServer Overview — Understand WMS vs WFS