| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214 |
- <?php
- // list_lookup.php
- // Turn off display of errors in the response, log instead
- ini_set('display_errors', '0');
- error_reporting(E_ALL);
- //require_once __DIR__ . '/rate_limit.php';
- // Convert PHP errors to exceptions so we can return JSON
- set_error_handler(function($severity, $message, $file, $line) {
- throw new ErrorException($message, 0, $severity, $file, $line);
- });
- // Always return JSON
- header('Content-Type: application/json; charset=utf-8');
- try {
- $raw = file_get_contents('php://input');
- $js = json_decode($raw, true);
- if (!$js || !isset($js['lat'], $js['lng'])) {
- http_response_code(400);
- echo json_encode(['ok' => false, 'error' => 'Missing lat/lng'], JSON_PRETTY_PRINT);
- exit;
- }
- $lat = (float)$js['lat'];
- $lng = (float)$js['lng'];
- $debug = !empty($js['debug']);
- // LIST PlanningOnline MapServer
- $service = 'https://services.thelist.tas.gov.au/arcgis/rest/services/Public/PlanningOnline/MapServer';
- /**
- * Query a MapServer layer by a point (lng/lat in EPSG:4326).
- * $returnGeometry: include geometry in the response
- * $extraParams: override/add raw ArcGIS params (e.g. resultRecordCount)
- */
- function arcgis_query($layerId, $lng, $lat, $outFields = '*', $returnGeometry = false, $extraParams = []) {
- global $service;
- $params = array_merge([
- 'f' => 'json',
- 'where' => '1=1',
- 'returnGeometry' => $returnGeometry ? 'true' : 'false',
- 'outFields' => $outFields,
- 'outSR' => '4326', // lat/lng for Leaflet
- 'geometryType' => 'esriGeometryPoint',
- 'spatialRel' => 'esriSpatialRelIntersects',
- 'inSR' => '4326',
- 'geometry' => json_encode(['x' => (float)$lng, 'y' => (float)$lat]),
- // Don't artificially limit multi-hit layers; override per-call below
- 'resultRecordCount'=> 100
- ], $extraParams);
- $url = "{$service}/{$layerId}/query";
- $ch = curl_init($url);
- curl_setopt_array($ch, [
- CURLOPT_POST => true,
- CURLOPT_POSTFIELDS => http_build_query($params),
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_TIMEOUT => 15,
- ]);
- $resp = curl_exec($ch);
- if ($resp === false) {
- $err = curl_error($ch);
- curl_close($ch);
- return ['error' => "cURL error: $err"];
- }
- curl_close($ch);
- return json_decode($resp, true);
- }
- // Layer IDs on PlanningOnline
- $LAYER_PARCELS = 2; // Cadastral Parcels
- $LAYER_LGA = 8; // Local Government Areas
- $LAYER_ZONES = 13; // Tasmanian Planning Scheme Zones
- $LAYER_CODES = 14; // Tasmanian Planning Scheme Code Overlays
- // --- Parcels (attributes) ---
- $parcelsResp = arcgis_query($LAYER_PARCELS, $lng, $lat, '*', false, ['resultRecordCount' => 1]);
- $parcelFeat = $parcelsResp['features'][0] ?? null;
- $parcelAttr = $parcelFeat['attributes'] ?? [];
- if (!$parcelFeat) {
- echo json_encode(['ok' => false, 'error' => 'No parcel found at that location']);
- exit;
- }
- // PID (field names vary)
- $pid = $parcelAttr['PID'] ?? $parcelAttr['PROPERTY_ID'] ?? null;
- // --- Parcel geometry (polygon) for map ---
- $parcelGeomResp = arcgis_query($LAYER_PARCELS, $lng, $lat, 'PID,OBJECTID', true, ['resultRecordCount' => 1]);
- $boundaryGeoJSON = null;
- if (!empty($parcelGeomResp['features'][0]['geometry']['rings'])) {
- // outSR=4326 → [lng, lat] ready for Leaflet/GeoJSON
- $rings = $parcelGeomResp['features'][0]['geometry']['rings'];
- $boundaryGeoJSON = [
- 'type' => 'Feature',
- 'geometry' => [
- 'type' => 'Polygon',
- 'coordinates' => $rings
- ],
- 'properties' => [
- 'pid' => $pid
- ]
- ];
- }
- // --- Compose Title Id ---
- $volume = $parcelAttr['VOLUME'] ?? null;
- $folio = isset($parcelAttr['FOLIO']) ? (string)$parcelAttr['FOLIO'] : null;
- $titleId = null;
- if ($volume && $folio !== null && $folio !== '') {
- $titleId = $volume . '/' . $folio;
- } else {
- $titleId = $parcelAttr['CT'] ?? $parcelAttr['CT_REFERENCE'] ?? null;
- }
- // --- Area ---
- $sqm = null;
- if (isset($parcelAttr['MEAS_AREA'])) {
- $sqm = (float)$parcelAttr['MEAS_AREA'];
- } elseif (isset($parcelAttr['COMP_AREA'])) {
- $sqm = (float)$parcelAttr['COMP_AREA'];
- } elseif (isset($parcelAttr['AREA_SQM'])) {
- $sqm = (float)$parcelAttr['AREA_SQM'];
- }
- $total_area = null;
- $area_sqm_label = null;
- $area_ha_label = null;
- if ($sqm !== null) {
- $ha = $sqm / 10000.0;
- $area_sqm_label = number_format($sqm, 0) . ' sqm';
- $area_ha_label = rtrim(rtrim(number_format($ha, 4, '.', ''), '0'), '.') . ' ha';
- $total_area = [
- 'sqm' => (float)$sqm,
- 'sqm_label' => $area_sqm_label,
- 'ha' => (float)$ha,
- 'ha_label' => $area_ha_label
- ];
- }
- $tenure = $parcelAttr['TENURE_TY'] ?? null;
- $lpi = $parcelAttr['LPI'] ?? null;
- $listGuid = $parcelAttr['LIST_GUID'] ?? null;
- // --- LGA / Council ---
- $lgaResp = arcgis_query($LAYER_LGA, $lng, $lat, '*', false, ['resultRecordCount' => 1]);
- $lgaAttr = $lgaResp['features'][0]['attributes'] ?? [];
- $council = $lgaAttr['LGA_NAME'] ?? $lgaAttr['NAME'] ?? $lgaAttr['COUNCIL'] ?? null;
- // --- Zones (multiple may intersect the point) ---
- $zonesResp = arcgis_query($LAYER_ZONES, $lng, $lat, '*', false, ['resultRecordCount' => 100]);
- $zoneFeatures = $zonesResp['features'] ?? [];
- $zoneNames = [];
- $schemeName = null;
- foreach ($zoneFeatures as $zf) {
- $a = $zf['attributes'] ?? [];
- foreach (['ZONE', 'ZONING', 'ZONE_NAME', 'ZONE_LABEL'] as $k) {
- if (!empty($a[$k])) { $zoneNames[] = $a[$k]; break; }
- }
- if (!$schemeName) {
- $schemeName = $a['LPS'] ?? $a['SCHEME'] ?? 'Tasmanian Planning Scheme';
- }
- }
- // --- Code overlays (multiple) ---
- $codesResp = arcgis_query($LAYER_CODES, $lng, $lat, '*', false, ['resultRecordCount' => 200]);
- $codeFeatures = $codesResp['features'] ?? [];
- $codeNames = [];
- foreach ($codeFeatures as $cf) {
- $a = $cf['attributes'] ?? [];
- foreach (['CODE', 'OVERLAY', 'OVERLAY_CODE', 'CODE_NAME', 'OVERLAY_DESC'] as $k) {
- if (!empty($a[$k])) { $codeNames[] = $a[$k]; break; }
- }
- }
- $out = [
- 'ok' => true,
- 'pid' => $pid,
- 'title_id' => $titleId,
- 'tenure' => $tenure,
- 'lpi' => $lpi,
- 'list_guid' => $listGuid,
- 'total_area' => $total_area, // structured with sqm/ha + labels
- 'area_sqm' => $area_sqm_label, // flat labels for your UI
- 'area_ha' => $area_ha_label,
- 'council' => $council,
- 'planning_scheme' => $schemeName,
- 'planning_zones' => array_values(array_unique(array_filter($zoneNames))),
- 'planning_codes' => array_values(array_unique(array_filter($codeNames))),
- 'boundary' => $boundaryGeoJSON
- ];
- if ($debug) {
- $out['debug'] = [
- 'parcel_attrs' => $parcelAttr,
- 'lga_attrs' => $lgaAttr,
- 'zones_count' => count($zoneFeatures),
- 'codes_count' => count($codeFeatures)
- ];
- }
- echo json_encode($out, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
- } catch (Throwable $e) {
- http_response_code(500);
- echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
- }
|