1

Using Python and the pyshp library, I am attempting to create a shapefile from the data below (stored in a list):

edge_list = [
    [-40.5, -20.666],
    [-39.849998, -18.700001],
    [-39.816002, -19.6],
    [-40.071999, -19.391001],
    [-40.150002, -19.933001],
    [-39.733002, -18.533001],
    [-39.833, -18.733],
    [-39.708, -18.419001],
    [-39.370998, -17.891001],
    [-39.200001, -17.417],
    [-39.216999, -17.299999],
    [-39.167, -17.083],
    [-39.049999, -16.433001],
    [-38.932999, -13.967],
    [-39.083, -16.583],
    [-39.0, -13.916],
    [-38.900002, -13.6],
]

Here is a segment of my code (where edge_list is the list above):

w = shapefile.Writer()
w.line(parts=[edge_list])
w.field("COMMON_ID", 'C')
w.save("test")

I get this:

enter image description here

But I want to get this:

enter image description here

Any hints?

EDIT: Here is the complete test code, but there is not much to it. The file "temp.csv" just contains the two columns of points shown above, separated by commas and with an extra line for headers (x, y).

import csv
import shapefile

data = csv.reader(open("test.csv", "rb"), delimiter = ',')
data.next() # skip header line
edge_list = []
for row in data:
    edge_list.append([float(row[0]), float(row[1])])

for e in range(len(edge_list)):
    print "x=", edge_list[e][0], "y=", edge_list[e][1]

w = shapefile.Writer()
w.line(parts=[edge_list])
w.field("COMMON_ID", 'C')
w.save("test")
maurobio
  • 1,480
  • 4
  • 27
  • 38
  • You need to [edit] your question and show exactly what the value of `edge_list` is when it's being passed as an argument in the `w.line(parts=[edge_list])` call. What's shown in your question now isn't valid Python data. – martineau Aug 22 '17 at 15:04
  • The data shown in my question is an exact print out of the contents of the edge_list variable: for e in range(len(edge_list): print edge_list[e]. – maurobio Aug 22 '17 at 19:15
  • OK, that's better, but still not exactly right—I'll fix it. – martineau Aug 22 '17 at 19:41
  • 1
    @martineau Apparently, it's not that simple. See OPs comment on my answer. Somewhere in there is `numpy.float32`s. – Scott Mermelstein Aug 22 '17 at 19:47
  • @Scott: That's interesting, but not sure how it relates to the problem. When I plot the points (now) shown in the question, I get exactly the shape shown in the image. I find it strange that that the coordinates need to be sorted to look right. **maurobio**: Where are these data points coming from...shouldn't they be in the proper order already? – martineau Aug 22 '17 at 19:58
  • I was reading the data as Numpy arrays, so the "TypeError: 'numpy.float32'". But using an ordinary Python list (as in the test code I just provided) only raises a similar error: "TypeError: 'float' object is not iterable". – maurobio Aug 22 '17 at 20:05
  • maurobio: Showing they were read in from a file isn't what I meant by "where are these data points coming from". I meant is it reasonable that they apparently don't specify coordinates along a continuous line in the order they are in initially (within the file). Sure you can sort them by `x` or `y` value...but does that make sense? – martineau Aug 22 '17 at 20:18
  • In fact, these data represent a _minimum spanning tree_ generated by Prim's algorithm from a list of geographic coordinates (longitudes, latitudes) read from a file. – maurobio Aug 22 '17 at 20:28
  • @maurobio You shouldn't edit the fix into your question - someone else who comes by and reads it won't know what to do. I recommend you roll back your latest edit, and either edit my answer, leave a comment on my answer, or post your own answer. All are better alternatives. – Scott Mermelstein Aug 22 '17 at 20:29
  • 1
    @Scott, right, I just did that. – maurobio Aug 22 '17 at 20:32

2 Answers2

2

Disclaimer: I've not used shapefiles or pyshp. But I know my way around drawing lines.

What I'm seeing is that it's drawing the lines in the order that you entered the points. It's connecting the dots, and that's the order your dots are given. What you need to do is re-order the points in edge_list.

In your case, your dots will look good if your y-variable is ordered.

So, try replacing this line:

w.line(parts=[edge_list])

with this:

w.line(parts=sorted(edge_list, key=lambda point: point[1]))

This will sort your points by the y-variable, and should draw the line the way you want.

Scott Mermelstein
  • 15,174
  • 4
  • 48
  • 76
  • You may instead need `w.line(parts=list(sorted(edge_list, key=lambda point: point[1])))`, since your example has `parts=[edge_list]`. I wouldn't expect the `[` `]` to be necessary, but if you need an extra level of list, that's how to get it. – Scott Mermelstein Aug 22 '17 at 15:36
  • Hi! Thanks for your fast reply. I do fully agree with your suggestion that the answer to my problem requires sorting of the points by the y-axis, however it does not work. When attempting to run the suggested code, I got the error: "TypeError: 'numpy.float32' object is not iterable". – maurobio Aug 22 '17 at 19:19
  • martineau has the right idea then - we need to see some actual code. Sorting will solve your problem. Showing us what's in your code will help us tell you how to sort it. – Scott Mermelstein Aug 22 '17 at 19:33
  • Try it with the `list` around it, as per my first comment. That's similar to the code you originally posted where you take edge_list, which should already be a list of points, and put `[` `]` around it. I just tested `sorted(edge_list, key=lambda pair: pair[1]), and it returns exactly what it should - a list of points, sorted on the y axis. So if you needed `parts=[edge_list]`, you'll need `parts=list(sorted(edge_list, key = lambda point: point[1])))` now. – Scott Mermelstein Aug 22 '17 at 20:16
  • I tried with the list function too, with same results. But I got it working simply sorting the list **before** passing it to shapefile.lines! It nows generates the correct line! Thank you very much to both you and martineau! – maurobio Aug 22 '17 at 20:18
  • Please make sure that `w.line(parts=[edge_list])` still works, and then just for fun, see if `w.line(parts=edge_list)` still works (i.e. produces the bad image). I think when you switched from reading it in via numpy to manually reading the file that you lost something important, and that it's no longer happy with your input. – Scott Mermelstein Aug 22 '17 at 20:21
1

I got a solution by simply sorting the list before passing it to shapefile.line().

Here is the complete, fully working code:

import csv
import shapefile

data = csv.reader(open("test.csv", "rb"), delimiter = ',')
data.next() # skip header line
edge_list = []
for row in data:
    edge_list.append([float(row[0]), float(row[1])])

## sort list before passing it to shapefile.line function
edge_list = list(sorted(edge_list, key=lambda point: point[1]))

for e in range(len(edge_list)):
    print "x=", edge_list[e][0], "y=", edge_list[e][1]

w = shapefile.Writer()
w.line(parts=[edge_list]) ## this now works OK!
w.field("COMMON_ID", 'C')
w.save("test")
maurobio
  • 1,480
  • 4
  • 27
  • 38