# Shapefile.jl writing .shp without coordinates

**URL:** <https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434>\
**Category:** Geo\
**Tags:** shapefile\
**Created:** [July 24, 2024, 5:27pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434 "2024-07-24T17:27:43Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![alejandromerchan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alejandromerchan/32/10500_2.png) [@alejandromerchan](https://discourse.julialang.org/u/alejandromerchan)\
**Post date:** [July 24, 2024, 5:27pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/1 "2024-07-24T17:27:43Z")

</div>

I loaded a Shapefile in Julia, transformed it to a datafame and then did some data cleaning, getting rid of some unneeded rows of data. I saved the shapefile again, with a new name, using “Shapefile.write” and tried to open the file in ArcGIS Pro. The layer did open, but it didn’t display on the map, saying that it didn’t have a coordinate system.

I tried to solve that in Julia, and I’m assuming that the transformation to Dataframe might have stripped that information and I need to recover it, but I don’t know how.

So, I’m guessing I have two options:

1. Is it possible to delete the rows of data without transforming the shapefile to a Dataframe? I’m basically a data analyst and I’m very familiar with DF, so that’s why I did it, but since the Shapefile seems compatible with the “Tables.jl” interface, I might be able to skip the transformation in some way.
2. Or is it possible to add a coordinate system to the file when I’m saving the dataframe as a shapefile? And if so, how do I know I’m using the correct one?

I’m sure this might be a fairly easy problem, but I’m new to GIS and I’m not still very familiar with all the ins and outs of shapefiles.

I’ll appreciate any insight someone can give me.

---

<div class="post-metadata">

**Author:** ![alex-s-gardner](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alex-s-gardner/32/30210_2.png) [@alex-s-gardner](https://discourse.julialang.org/u/alex-s-gardner)\
**Post date:** [July 24, 2024, 6:10pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/2 "2024-07-24T18:10:49Z")

</div>

Welcome @alejandromerchan, are you able to post the shapefile and a MWE?

As a quick fix you can always assign a coordinate system of a shapefile once it’s loaded into ArcGIS.

---

<div class="post-metadata">

**Author:** ![alejandromerchan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alejandromerchan/32/10500_2.png) [@alejandromerchan](https://discourse.julialang.org/u/alejandromerchan)\
**Post date:** [July 24, 2024, 7:16pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/3 "2024-07-24T19:16:31Z")

</div>

The file I’m using comes from this [website]. ([California Public Land Survey System - California Pesticide Information Portal](https://www.cdpr.ca.gov/docs/emon/grndwtr/plss_shapefiles.htm)) Is the statewide file, on the lower left corner, but I’m assuming any of them would have the same issue (I haven’t checked).

MWE

> using DataFrames, Shapefile  
> table = Shapefile.Table(“./Statewide\_CA\_PLSS\_NAD83AlbersCA\_20240718.shp”);  
> plss = DataFrame(table)  
> # Filter data where column SECTION has “OE”, “TA” or “EA” (Sections from the Delta, Tahoe and Salton Sea)  
> subset!(plss, :SECTION =\> ByRow(x → x ∉ [“EA”, “OE”, “TA”]));  
> # Save file as shapefile, with a different name, on the same folder  
> Shapefile.write(“plss\_modified.shp”, plss)

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [July 24, 2024, 7:21pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/4 "2024-07-24T19:21:46Z")

</div>

You may want to try GMT.jl. When it reads a shapefile (`D = gmtread("file.shp")`) data is returned a _simple_ tabular type (a [GMTdataset](https://www.generic-mapping-tools.org/GMTjl_doc/documentation/general/types/index.html#dataset_type)) (or a vector of them). Numeric data is stored in the matrix field _data_ so you can do your matrix manipulations and at the save back to _.shp_ with `gmtwrite("filenmae.shp", D)`.

---

<div class="post-metadata">

**Author:** ![alejandromerchan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alejandromerchan/32/10500_2.png) [@alejandromerchan](https://discourse.julialang.org/u/alejandromerchan)\
**Post date:** [July 24, 2024, 7:28pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/5 "2024-07-24T19:28:30Z")

</div>

Thanks for the suggestion, I’ve used `GMT.jl` in the past, for some plotting, but never for data manipulation. I’ll take a look.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [July 24, 2024, 7:30pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/6 "2024-07-24T19:30:24Z")

</div>

See also  
[https://www.generic-mapping-tools.org/GMTjl\_doc/documentation/utilities/getbyattrib/index.html#getbyattrib](https://www.generic-mapping-tools.org/GMTjl_doc/documentation/utilities/getbyattrib/index.html#getbyattrib)  
&  
[https://www.generic-mapping-tools.org/GMTjl\_doc/tutorials/vector\_shp/vector\_shp/](https://www.generic-mapping-tools.org/GMTjl_doc/tutorials/vector_shp/vector_shp/)

---

<div class="post-metadata">

**Author:** ![alex-s-gardner](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alex-s-gardner/32/30210_2.png) [@alex-s-gardner](https://discourse.julialang.org/u/alex-s-gardner)\
**Post date:** [July 24, 2024, 7:56pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/7 "2024-07-24T19:56:40Z")

</div>

OK, this issue is that `Shapefile.Table() does not retain the coordinate info. The simple solution is to use [GeoDataFrames](https://github.com/evetion/GeoDataFrames.jl).

use: `df = GeoDataFrames.read(path2shp)` then everything should work as expected.

If you want to manipulate or create geometries using the JuliaGeo ecosystem  
there is a helpful tutorial here: [Creating Geometry | GeometryOps.jl](https://juliageo.org/GeometryOps.jl/stable/tutorials/creating_geometry)

---

<div class="post-metadata">

**Author:** ![alejandromerchan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alejandromerchan/32/10500_2.png) [@alejandromerchan](https://discourse.julialang.org/u/alejandromerchan)\
**Post date:** [July 24, 2024, 8:34pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/8 "2024-07-24T20:34:38Z")

</div>

Thanks @alex-s-gardner but not quite there yet, I’m probably missing something. The files opened in ArcGIS without coordinates again, I might have to dig some more. I do like that with `GeoDatFrames.jl` I get the `DF` automatically and can do the manipulations with the same syntax.

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [July 24, 2024, 8:42pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/9 "2024-07-24T20:42:06Z")

</div>

@alejandromerchan the problem is that .shp files do not require CRS information in their original specification. Later on, people started to include .prj files alongside the .shp, which include a CRS string.

Currently, Shapefile.jl assumes that this string is a ESRI WKT1 string when it encounters a .prj file (even though that is not necessarily the case), and it is probably not writing the .prj file in the `write` function for some reason?

I suggest that you open an issue in Shapefile.jl if that is the case.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [July 24, 2024, 8:52pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/10 "2024-07-24T20:52:47Z")

</div>

> [@alex-s-gardner](#):
>
> The simple solution is to use [GeoDataFrames](https://github.com/evetion/GeoDataFrames.jl).

It might, but this also seems pretty simple (I renamed the shape file to make the commands shorter)

```julia
using GMT

D = gmtread("Statewide_CA_PLSS_NAD83AlbersCA_20240718.shp.zip");
D2 = filter(D, SECTION=["OE", "TA", "EA"]);
gmtwrite("OETAEA.shp", D2)
viz(D2, name="OEATEA.png")

```

 ![OEATEA](https://global.discourse-cdn.com/julialang/original/3X/0/e/0ee984fa2eac271b0e1accb7a134e0482effa0a3.png)

---

<div class="post-metadata">

**Author:** ![alex-s-gardner](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alex-s-gardner/32/30210_2.png) [@alex-s-gardner](https://discourse.julialang.org/u/alex-s-gardner)\
**Post date:** [July 24, 2024, 9:17pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/11 "2024-07-24T21:17:36Z")

</div>

This works for me:

```julia
using Shapefile
using Downloads: download
using GeoDataFrames
using ArchGDAL
using DataFrames

#https://juliageo.org/GeometryOps.jl/dev/tutorials/creating_geometry

# paths 2 files 
url = "https://calpip.cdpr.ca.gov/content/groundwater/shapefiles/Statewide_CA_PLSS_NAD83AlbersCA_20240718.zip"
local_zip = "/Users/gardnera/Downloads/junk.zip";

path2shp = "/Users/gardnera/Downloads/junk/Statewide_CA_PLSS_NAD83AlbersCA_20240718.shp"
path2shp_new = replace(path2shp, ".shp" => "_new.shp")

# download and unzip
download(url, local_zip)
run(`unzip $local_zip`)

df = GeoDataFrames.read(path2shp)
df = subset!(df, "SECTION" => ByRow(x -> x ∉ ["EA", "OE", "TA"]));

# there is one geometry that does not match the others
same_geometry_index = typeof(df.geometry[1]) .== typeof.(df.geometry)
Shapefile.write(path2shp_new , df[same_geometry_index,:];force=true)

```

 ![Screenshot 2024-07-24 at 2.11.15 PM](https://global.discourse-cdn.com/julialang/original/3X/3/8/38802931b8c9d9d189350238b9fbdaedc1021f0e.jpeg)

---

<div class="post-metadata">

**Author:** ![alex-s-gardner](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alex-s-gardner/32/30210_2.png) [@alex-s-gardner](https://discourse.julialang.org/u/alex-s-gardner)\
**Post date:** [July 24, 2024, 9:19pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/12 "2024-07-24T21:19:44Z")

</div>

> [@joa-quim](#):
>
> It might, but this also seems pretty simple

@joa-quim GMT is a great option as well.

---

<div class="post-metadata">

**Author:** ![visr](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/visr/32/17204_2.png) [@visr](https://discourse.julialang.org/u/visr)\
**Post date:** [July 24, 2024, 9:35pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/13 "2024-07-24T21:35:01Z")

</div>

> [@alejandromerchan](#):
>
> assuming that the transformation to Dataframe might have stripped that information

That’s right: [subset shp while preserve proj · Issue #116 · JuliaGeo/Shapefile.jl (github.com)](https://github.com/JuliaGeo/Shapefile.jl/issues/116)

You could keep it manually using `GeoInterface.crs(handle)`, and then supply it to the Shapefile writer manually when writing out again.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [July 24, 2024, 9:36pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/14 "2024-07-24T21:36:06Z")

</div>

Thanks. I think I missed understanding that the OP wanted those NOT in `["OE", "TA", "EA"]`. I don’t have that implemented in GMT.jl, but it should not be difficult to do.

---

<div class="post-metadata">

**Author:** ![alejandromerchan](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alejandromerchan/32/10500_2.png) [@alejandromerchan](https://discourse.julialang.org/u/alejandromerchan)\
**Post date:** [July 24, 2024, 9:39pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/15 "2024-07-24T21:39:39Z")

</div>

Yes, @juliohm, the problem is somewhere there. I took the original `.prj` file, renamed it, moved it to the folder with my modified `.shp` file and it all worked. I’ll file an issue with `Shapefile.jl`.

---

<div class="post-metadata">

**Author:** ![alex-s-gardner](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/alex-s-gardner/32/30210_2.png) [@alex-s-gardner](https://discourse.julialang.org/u/alex-s-gardner)\
**Post date:** [July 24, 2024, 10:04pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/16 "2024-07-24T22:04:40Z")

</div>

@visr how would this work? I can’t seem to extract the crs from the shapefile.

---

<div class="post-metadata">

**Author:** ![joa-quim](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joa-quim/32/227_2.png) [@joa-quim](https://discourse.julialang.org/u/joa-quim)\
**Post date:** [July 24, 2024, 10:15pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/17 "2024-07-24T22:15:59Z")

</div>

> [@joa-quim](#):
>
> I don’t have that implemented in [GMT.jl](https://juliahub.com/ui/Packages/General/GMT), but it should not be difficult to do.

Done in master.

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [July 24, 2024, 10:26pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/18 "2024-07-24T22:26:30Z")

</div>

We should probably embed the crs in all the Shapefile.jl geometry objects so it can’t get lost.

Not difficult to do.

The other discussed alternative is DataAPI.metadata returning the crs fir a DataFrame. But it won’t work for e.g. a NamedTuple table, so embedding the crs in Polygon and other geoms is probably the best option.

---

<div class="post-metadata">

**Author:** ![asinghvi17](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/asinghvi17/32/8272_2.png) [@asinghvi17](https://discourse.julialang.org/u/asinghvi17)\
**Post date:** [July 25, 2024, 4:40am UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/19 "2024-07-25T04:40:29Z")

</div>

If DataFrames’ table materializer preserves metadata then we may just want to hook Shapefile.Table up to DataAPI for a quick fix…

---

<div class="post-metadata">

**Author:** ![Raf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raf/32/3383_2.png) [@Raf](https://discourse.julialang.org/u/Raf)\
**Post date:** [August 4, 2024, 4:08pm UTC](https://discourse.julialang.org/t/shapefile-jl-writing-shp-without-coordinates/117434/20 "2024-08-04T16:08:36Z")

</div>

Its not very hard tecnically, but its a commitment to updating the ecosystem to handle the metadata.

We would need to document the metadata names in the GeoInterface.jl docs and implement checking them everywhere.
