calvinmetcalf / shapefile-js

Convert a Shapefile to GeoJSON. Not many caveats.
http://calvinmetcalf.github.io/shapefile-js/
714 stars 228 forks source link

British National Grid prj - I don't know that shp type #189

Open alexcroox opened 1 year ago

alexcroox commented 1 year ago

I have 2 zip shapefiles, I believe they are both using the British National Grid (EPSG:27700)

One of them works fine, it's .prj contents are:

PROJCS["British_National_Grid",GEOGCS["GCS_OSGB_1936",DATUM["D_OSGB_1936",SPHEROID["Airy_1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]

The other does not work and has this:

PROJCS["British National Grid",GEOGCS["Ordnance Survey Great Brit",DATUM["Ordnance Survey Great Brit",SPHEROID["Airy 1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Decimal_Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]

They seem to have slightly different values:

PROJCS: British_National_Grid vs British National Grid GEOGCS: GCS_OSGB_1936 vs Ordnance Survey Great Brit DATUM: D_OSGB_1936 vs Ordnance Survey Great Brit

I cannot control the output of these files as the latter comes from the Scottish goverment website.

Is there anything I can do to correct it during the processing with this lib?

I've uploaded "good" and "bad" zip files for reference

bad.zip good.zip

It's not an invalid shapefile because uploading to https://mapshaper.org seems to process it fine

alexcroox commented 1 year ago

OK cleaning up the bad.zip and only leaving the single shapefile files inside seems to work. I'm now thinking the library is choking on non shapefile files. I'll manually extract the contents as a workaround and manually feed in the .shp and .dbf

Before:

image

After:

image

calvinmetcalf commented 1 year ago

it's the shp_EFAs_20221003 file that it is choking on as it's shape type is...null

calvinmetcalf commented 1 year ago

is that even legal in shapefiles? I thought no and had a test that checked that we errored, but looking at the spec it doesn't say you can't

alexcroox commented 1 year ago

Thanks for looking! I'll be honest I'm very new to shapefiles, where can I find the shape type equalling "null"?. Happy to put in my own checks for excluding those files to avoid it erroring.

calvinmetcalf commented 1 year ago

well the issue is that when you open a zipfile with 4 shapefiles in it this library tries to convert all 4 of them, and if one of them breaks then the whole thing breaks so that's why you're getting the error. So I think it's technically an error in this library.

alexcroox commented 1 year ago

Leaving a note in case anyone has weird behaviour with British National Grid shapefiles.

I have a scottish shapefile and the resulting geojson polygons are offset from their real positions by ~50 meters or so. The .prj has this:

PROJCS["British National Grid",GEOGCS["Ordnance Survey Great Brit",DATUM["Ordnance Survey Great Brit",SPHEROID["Airy 1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Decimal_Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]

If I replace it with a shapefile .prj I know is acurrately parsed, it lines up perfectly with the terrain:

PROJCS["British_National_Grid",GEOGCS["GCS_OSGB_1936",DATUM["D_OSGB_1936",SPHEROID["Airy_1830",6377563.396,299.3249646]],PRIMEM["Greenwich",0.0],UNIT["Decimal_Degree",0.0174532925199433]],PROJECTION["Transverse_Mercator"],PARAMETER["False_Easting",400000.0],PARAMETER["False_Northing",-100000.0],PARAMETER["Central_Meridian",-2.0],PARAMETER["Scale_Factor",0.9996012717],PARAMETER["Latitude_Of_Origin",49.0],UNIT["Meter",1.0]]

I have no idea why whatever tool created the 1st .prj file has created it in such a way, but from now on I'm going to parse the .prj file, and replace it's contents with the 2nd if I detect it's British National Grid just to be sure.

Despite the other differences I narrowed it down to one value that that lines up the polygons with the terrain correctly: DATUM["Ordnance Survey Great Brit" -> DATUM["D_OSGB_1936"

I understand very little about projection systems so maybe there's a better way of fixing it but for now I'm going to do a text replacement if I detect it 🤷

Using jszip package:

// Find filename in list of zip files that has .prj extension
let prjFileName = Object.keys(zip.files).find((item) => item.endsWith(".prj"));

const prjFile = zip.file(prjFileName);

if (prjFile) {
  const prjFileContent = await prjFile.async("string");

  if (prjFileContent.includes('DATUM["Ordnance Survey Great Brit"')) {
    zip.file(
      prjFileName,
      prjFileContent.replace(
        'DATUM["Ordnance Survey Great Brit"',
        'DATUM["D_OSGB_1936"'
      )
    );
  }
}