31 lines
6.1 KiB
JavaScript
31 lines
6.1 KiB
JavaScript
const fs=require('node:fs'),vm=require('node:vm'),assert=require('node:assert/strict');
|
|
const context={window:{}};assert.equal(fs.readFileSync('dist/maps.js','utf8'),fs.readFileSync('src/maps.js','utf8'),'地图源素材与构建产物必须一致,请在地图生成完成后重新构建');vm.runInNewContext(fs.readFileSync('dist/maps.js','utf8'),context);const data=context.window.MAP_DATA,level=data.levels[0],source=JSON.parse(fs.readFileSync('research/china.json','utf8'));
|
|
const expected=['110000','120000','130000','140000','150000','210000','220000','230000','310000','320000','330000','340000','350000','360000','370000','410000','420000','430000','440000','450000','460000','500000','510000','520000','530000','540000','610000','620000','630000','640000','650000','710000','810000','820000'];
|
|
assert.deepEqual(Array.from(level.regions,r=>r.id).sort(),expected.slice().sort());
|
|
const regions=level.regions.map(r=>{const f=source.features.find(f=>String(f.properties.adcode)===r.id),raw=f.geometry.type==='MultiPolygon'?f.geometry.coordinates:[f.geometry.coordinates];assert.equal(r.name,f.properties.name);assert.equal(r.rings.length,raw.length,r.name+'不得丢弃独立外环');for(const ring of r.rings){assert.ok(ring.length>=3);assert.ok(ring.every(p=>p.length===2&&p.every(Number.isFinite)));let area=0;for(let i=0;i<ring.length;i++)area+=ring[i][0]*ring[(i+1)%ring.length][1]-ring[(i+1)%ring.length][0]*ring[i][1];assert.ok(Math.abs(area)>1e-10,r.name+'存在退化外环');}return {id:r.id,name:r.name,sourcePolygonCount:raw.length,packagedPolygonCount:r.rings.length};});
|
|
const maritime=source.features.find(f=>f.properties.adcode==='100000_JD');assert.equal(level.decorations.length,maritime.geometry.coordinates.length);
|
|
assert.ok(level.edges.some(e=>e[2]===1&&(level.regions[e[0]].id==='710000'||level.regions[e[1]].id==='710000')));
|
|
const report={passed:true,provinceCount:34,regions,maritimeSegments:level.decorations.length,taiwanIncluded:true,hongKongIncluded:true,macaoIncluded:true,countyAndPrefectureLimitation:'台湾省地级轮廓接口404;无对应地级关卡。直辖市、港澳不设地级单元关卡。',scope:'验证34项名称/代码及源数据外环未丢失,不等于标准地图审图或所有最新区划/全部岛礁的独立权威核验。',mapContentReference:'https://www.gov.cn/gongbao/content/2023/content_5752310.htm'};
|
|
fs.writeFileSync('artifacts/map-audit.json',JSON.stringify(report,null,2));console.log('地图核对通过:34省级行政区,含台湾/香港/澳门;全部源外环保留,海域线层'+level.decorations.length+'段。');
|
|
// A province endpoint lists all immediate children. Audit the frozen source
|
|
// list, including province-administered county-level peers, not only counts.
|
|
const provinceIndex=JSON.parse(fs.readFileSync('research/prefecture-map-index.json','utf8'));
|
|
assert.equal(provinceIndex.levels.length,27);let provinceDirectPieces=0,prefecturePieces=0;
|
|
for(const entry of provinceIndex.levels){
|
|
const lv=data.levels.find(l=>l.id===entry.id);assert.ok(lv&&lv.kind==='prefecture');
|
|
assert.deepEqual(Array.from(lv.regions,r=>r.id).sort(),entry.regions.map(r=>r.id).sort(),entry.name+'不得过滤省直辖地区或遗漏源子区域');
|
|
assert.equal(new Set(lv.regions.map(r=>r.id)).size,lv.regions.length);
|
|
for(const r of lv.regions){const expected=entry.regions.find(v=>v.id===r.id);assert.equal(r.name,expected.name);assert.equal(r.unitType,expected.unitType);assert.equal(r.rings.length,expected.rings,r.name+'源外环必须保留');assert.equal((r.holes||[]).length,expected.holes,r.name+'源内环必须保留');if(r.unitType==='province-direct')provinceDirectPieces++;else prefecturePieces++;for(const ring of r.rings.concat(r.holes||[]))assert.ok(ring.length>=3&&ring.every(p=>p.every(Number.isFinite)));if(r.holes&&r.holes.length)assert.ok(r.physics&&r.physics.length);}
|
|
const direct=entry.regions.filter(r=>r.unitType==='province-direct').length;
|
|
assert.equal(lv.directRegionCount||0,direct);if(direct){assert.equal(lv.playLabel,'省域拼图');assert.ok(lv.unitCaption.includes('省直辖'));}
|
|
}
|
|
assert.equal(prefecturePieces,333);assert.equal(provinceDirectPieces,30);
|
|
const hainan=data.levels.find(l=>l.id==='460000');assert.ok(hainan.mapInsets&&hainan.mapInsets.length===1);assert.equal(hainan.mapInsets[0].id,'460300');assert.ok(hainan.mapInsets[0].label.includes('缩放移位示意'));assert.ok(hainan.scopeNote.includes('三沙'));
|
|
report.prefectureLevels=27;report.prefecturePieces=prefecturePieces;report.provinceDirectPieces=provinceDirectPieces;report.provinceChildPieces=prefecturePieces+provinceDirectPieces;
|
|
console.log('省内完整性核对通过:27关、333个地级单元及30个省直辖地区,全部源子区域/外环/内环保留。');
|
|
// The detailed-level index records source IDs, names and exterior/hole counts.
|
|
const detailIndex=JSON.parse(fs.readFileSync('research/detail-map-index.json','utf8'));
|
|
assert.equal(detailIndex.levels.length,7);let detailPieces=0;
|
|
for(const entry of detailIndex.levels){const lv=data.levels.find(l=>l.id===entry.id);assert.ok(lv&&lv.kind===entry.kind);assert.equal(lv.regions.length,entry.regions.length);for(const r of lv.regions){const raw=entry.regions.find(v=>v.id===r.id);assert.ok(raw);assert.equal(r.name,raw.name);assert.equal(r.rings.length,raw.rings,r.name+'源外环必须保留');assert.equal((r.holes||[]).length,raw.holes||0,r.name+'源内环必须保留');for(const ring of r.rings.concat(r.holes||[])){assert.ok(ring.length>=3);assert.ok(ring.every(p=>p.every(Number.isFinite)));}if(r.holes&&r.holes.length)assert.ok(r.physics&&r.physics.length);detailPieces++;}}
|
|
report.detailedLevels=detailIndex.levels.map(l=>({id:l.id,name:l.name,kind:l.kind,pieces:l.regions.length,source:l.source,snapshot:l.snapshot}));report.detailedPieces=detailPieces;report.countyAndPrefectureLimitation='四直辖市县区、香港/澳门分区、台湾县市细分关卡已开放;台湾远海岛屿移位示意,金门/连江不重复列入台湾。';fs.writeFileSync('artifacts/map-audit.json',JSON.stringify(report,null,2));console.log('细分地图核对通过:7个新增关卡、'+detailPieces+'块,源外环/内环及类型标注完整。');
|