PHP搜索countryBorders.geo.json匹配iso_a3返回边界坐标问题求解
TL;DR: 这个任务看起来完全没有必要,我对此表示抱歉。
概述
我已经完成了一个作品集项目,但不符合规范要求,所以需要按照指引重写大部分代码。
这是一个使用leaflet.js开发的地名词典网站项目,要求必须使用jQuery、原生JavaScript和PHP程序访问多个API,填充不同的Bootstrap模态框。我已经完成的版本我个人认为完全满足这个作品集项目的需求。
你可以在这里访问测试: https://kizzysinar.co.uk/project1/index.html
我拿到了countryBorders.geo.json文件,其中包含多个地理要素,结构如下:
{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "name": "Bahamas", "iso_a2": "BS", "iso_a3": "BHS", "iso_n3": "044" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [-77.53466,23.75975], [-77.78,23.71], [-78.03405,24.28615], [-78.40848, 24.57564], [-78.19087,25.2103], [-77.89,25.17], [-77.54,24.34], [-77.53466,23.75975] ] ], [ [ [-77.82,26.58], [-78.91,26.42], [-78.98,26.79], [-78.51,26.87], [-77.85,26.84], [-77.82,26.58] ] ], [ [ [-77,26.59], [-77.17255,25.87918], [-77.35641,26.00735], [-77.34,26.53], [-77.78802,26.92516], [-77.79,27.04], [-77,26.59] ] ] ] } }, {..... 更多要素 ] }
我原本的实现是直接用这个文件填充<select>下拉选项、生成地图边界,具体用法如下:
- 填充
<select>下拉框:
result['data'].features.forEach(function (feature) { $("<option>", { value: feature.properties.iso_a3, text: feature.properties.name }).appendTo("#countrySelect")
- 由于是本地存储文件,我用
leaflet.ajax解析countryBorders.geo.json中的坐标,为每个feature生成分面图层:
var geoJSONLayer = new L.GeoJSON.AJAX('vendors/json/countryBorders.geo.json',{ style: myStyle, onEachFeature: onEachFeature }).addTo(mymap);
用户选择国家时,我会将选中项的value: feature.properties.iso_a3通过AJAX请求发送到REST Countries API,用返回的经纬度设置地图视图:
function setView() { $.ajax({ url: "libs/php/locateCountry.php", type: 'POST', dataType: 'json', data: { iso: $('#countrySelect option:selected').val() }, success: function(result) { if (result.status.name == "ok") { mymap.setView(result['coordinates']); } },
需求变更
现在要求必须通过jQuery/AJAX请求PHP接口,访问本地存储的countryBorders.geo.json,返回与指定iso_a3匹配的坐标。
具体流程要求:用户选择国家后,将选中项的value: iso_a3通过AJAX发送给getCountryBorders.php,PHP用file_get_contents读取JSON文件,匹配feature.properties.iso_a3 == 传入的iso参数,返回对应的边界坐标,AJAX请求成功后用返回的坐标生成L.geoJSON图层,触发Bootstrap模态框。
流程梳理:<select>/<option:selected> -> value: iso_a3 -> AJAX 携带iso: iso_a3发起请求 -> PHP读取countryBorders.geo.json -> 匹配feature.properties.iso_a3 == 'iso' -> 返回边界坐标 -> AJAX success回调生成对应国家的L.geoJSON边界图层。
现有代码及报错
示例PHP参考代码
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL); $executionStartTime = microtime(true); $result=file_get_contents('C:\xampp\htdocs\project1\vendors\json\countryBorders.geo.json'); $decode = json_decode($result,true); $output['status']['code'] = "200"; $output['status']['name'] = "ok"; $output['status']['description'] = "success"; $output['status']['returnedIn'] = intval((microtime(true) - $executionStartTime) * 1000) . " ms"; $output['data'] = $decode; header('Content-Type: application/json; charset=UTF-8'); echo json_encode($output); ?>
自行编写的PHP代码(有问题)
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL); $start=microtime( true ); $file=file_get_contents('vendors\json\countryBorders.geo.json'); $json=json_decode($file, true); foreach ($json->features->properties as $object) { if ($object->iso_a3 == $_REQUEST['iso']) { $output=array( 'border' => $object->coordinates, 'status' => array( 'code' => 200, 'name' => 'ok', 'description' => 'success', 'returnedIn' => intval( ( microtime( true ) - $start ) * 1000 ) . 'ms' ), ); } else { $output=$json; } } header('Content-Type: application/json; charset=UTF-8'); exit( json_encode( $output ) ); ?>
前端AJAX代码
function setView() { $.ajax({ url: "libs/php/getCountryBorders.php", type: 'POST', dataType: 'json', data: { iso: $('#countrySelect option:selected').val() //从option的value中获取iso编码 }, success: function(result) { if (result.status.name == "ok") { console.log(result['border']); //当前仅打印返回结果 } }, error: function(jqXHR, textStatus, errorThrown) { console.log(jqXHR); } }); };
报错信息
{readyState: 4, getResponseHeader: ƒ, getAllResponseHeaders: ƒ, setRequestHeader: ƒ, overrideMimeType: ƒ, …} script.js:69 parsererror script.js:70 SyntaxError: Unexpected token < in JSON at position 0 at parse (<anonymous>) at jquery.min.js:2 at l (jquery.min.js:2 at XMLHttpRequest.<anonymous> (jquery.min.js:2)
解决方案
错误原因
json_decode($file, true)第二个参数为true时返回的是关联数组,不是对象,不能用->访问属性,必须用[]- 遍历层级错误:应该遍历
$json['features']数组,每个元素的iso_a3存放在$feature['properties']['iso_a3'],坐标存放在$feature['geometry']['coordinates'] - 原有遍历逻辑错误:每次循环都会覆盖
$output变量,未匹配到对应国家时返回了整个JSON结构,格式和前端预期不符 - 路径用反斜杠仅兼容Windows,建议换成正斜杠避免跨环境问题
修正后的PHP代码
<?php ini_set('display_errors', 'On'); error_reporting(E_ALL); $start = microtime(true); // 可根据自己的项目路径调整为绝对路径,避免相对路径定位错误 $jsonPath = 'C:/xampp/htdocs/project1/vendors/json/countryBorders.geo.json'; $fileContent = file_get_contents($jsonPath); $jsonData = json_decode($fileContent, true); // 初始化默认返回结果 $output = [ 'status' => [ 'code' => 404, 'name' => 'error', 'description' => '未匹配到对应国家边界', 'returnedIn' => intval((microtime(true) - $start) * 1000) . 'ms' ] ]; // 遍历features数组匹配iso_a3 foreach ($jsonData['features'] as $feature) { if ($feature['properties']['iso_a3'] === $_REQUEST['iso']) { $output['border'] = $feature['geometry']['coordinates']; $output['status'] = [ 'code' => 200, 'name' => 'ok', 'description' => 'success', 'returnedIn' => intval((microtime(true) - $start) * 1000) . 'ms' ]; // 匹配到就终止循环,不需要继续遍历 break; } } header('Content-Type: application/json; charset=UTF-8'); echo json_encode($output); exit; ?>
内容的提问来源于stack exchange,提问作者Ranisimo

