0

Right, I made a hash of asking this question when I was fried at the back end of yesterday so I'm doing it properly now.

The block below converts some data into lat/long info and then there's an if statement where I can grab certain parts of a large JSON file which there's a section of below.

What I want to do it take the forklift data, convert it's position (which is x and y meters from a known lat/long into an actual lat/long using Pythagoras) and then use the if statement to only print certain parts of the JSON (name, time, converted position). The JSON consists of people & machine info and I want to separate them.

"id": "b4994c877c9c",
"name": "forklift_0001",  <---forklift data used in IF statement
"areaId": "Tracking001",
"areaName": "hall_1",
"color": "#FF0000",
"coordinateSystemId": "CoordSys001",
"coordinateSystemName": null,
"covarianceMatrix": [
    0.47,
    0.06,
    0.06,
    0.61
],
"position": [
    33.86,    <---position data converted from known lat/long, X then Y.
    33.07,
    2.15
],
"positionAccuracy": 0.36,
"positionTS": 1489363199493,
"smoothedPosition": [
    33.96,
    33.13,
    2.15
],
"zones": [
    {
        "id": "Zone001",
        "name": "Halli1"

The problem I have seems to be that the first print statement is a float and to be able to pass the result into the if statement it needs to be a string.

Here's the code I have

for f in file_list:
print('Input file: ' + f) # Replace with desired operations

with open(f, 'r') as f:

    distros = json.load(f)
    output_file = 'forklift_0001_parse' + str(output_nr) + '.csv' #output file name may be changed

    with open(output_file, 'w') as text_file:
        for distro in distros:      
            position = distro['position']
            R = 6378.1 #Radius of the Earth
            brng = 1.57 #Bearing is 90 degrees converted to radians.
            d = math.sqrt((position[0]*position[0] + position[1]*position[1]) + 0.00303) #Pythagoras formula to work distance from ref lat/long point

            lat1 = math.radians(60.477719)#Reference lat point 
            lon1 = math.radians(26.941589)#Reference long point 

            lat2 = math.asin(math.sin(lat1)*math.cos(d/R) + 
            math.cos(lat1)*math.sin(d/R)*math.cos(brng))

            lon2 = lon1 + math.atan2(math.sin(brng)*math.sin(d/R)*math.cos(lat1),
            math.cos(d/R)-math.sin(lat1)*math.sin(lat2))

            lat2 = math.degrees(lat2)
            lon2 = math.degrees(lon2)
            print((lat2, lon2), file=text_file) 

            if distro['name'].startswith(('Trukki_0001')): #choose desired parameters to be parsed
                print (str(distro['name']))

                print(distro['name'] + ',' + str(distro['positionTS']) + ',' + str(distro['position']) + ',' + str(distro['lat2']), file=text_file)
                #replace desired roots between square brackets

    print('Output written to file: ' + output_file)
    output_nr = output_nr + 1

The error this gives is

Traceback (most recent call last):
File "position_parse_3.py", line 45, in <module>
print(distro['name'] + ',' + str(distro['positionTS']) + ',' + 
str(distro['position']) + ',' + str(distro['lat2']), file=text_file)
KeyError: 'lat2'

I am completely stuck and have tried a few ways to solve it. I've tried to convert them from floats earlier in the script that first prints statement but that did not work (can't convert them implicitly) and I am also concerned I'll lose the accuracy of the result.

If someone could help me pass the result of the lat/long calculation into the IF statement, i would be eternally grateful.

exmatelote
  • 115
  • 1
  • 14
  • Ok, the inclusion of the JSON makes this a much better starting point for this :) But your issue seems to still be misdiagnosed. Even if you did pass the result of your calculation as a key, you won't get this to work because there are _no_ keys in that dictionary that resemble a float. – roganjosh Jun 28 '18 at 07:08
  • You want something like snap-to-grid here as the wider issue you're tackling? If so, the JSON is perhaps back-to-front for what you want, so we'll need to do some transformation beforehand. – roganjosh Jun 28 '18 at 07:11

1 Answers1

0

This is tough to answer because I think you're going about the issue the wrong way. If we take a step back and define a more simple dictionary:

json_response = {'loc_a': [20, 30], 'loc_b': [10, 50], 'loc_c': [30, 40]}

The only valid ways to access the data by key would be json_response['loc_a'], json_response['loc_b'] or json_response['loc_c']. There are no keys in that dictionary that resemble a numerical value; they're contained in the values stored against the keys. The same applies to your current situation; there are no numerical-looking keys, so it's not possible to do what you want to do. Even if there were, you'll be confronted with rounding errors etc. if you converted your lat/long to a string to use as a key.

You've mentioned a type error in both questions, but what you're getting is KeyError. These are very different things, and there's no reason to believe that the KeyError is due to typing at all. json_response[40] and json_response["40"] still won't work in my example. To answer the actual question, you would use + str(distro[str(lat2)]) to use the string representation of your float, but it still won't work because of the aforementioned issue.

Your problem is actually quite a lot more complex. Searching for those lat/longs in a dictionary is not simple. I can't think of a neat way of storing these locations; it looks like you'll have to traverse the JSON each time. There are certainly more efficient ways of going about this, but for illustration:

from math import radians, cos, sin, asin, sqrt

def haversine(lon1, lat1, lon2, lat2):
    """
    taken from: https://stackoverflow.com/a/4913653/4799172
    """
    # convert decimal degrees to radians 
    lon1, lat1, lon2, lat2 = map(radians, [lon1, lat1, lon2, lat2])

    # haversine formula 
    dlon = lon2 - lon1 
    dlat = lat2 - lat1 
    a = sin(dlat/2)**2 + cos(lat1) * cos(lat2) * sin(dlon/2)**2
    c = 2 * asin(sqrt(a)) 
    r = 6371 # Radius of earth in kilometers. Use 3956 for miles
    return c * r


json_response = {'loc_a': [20, 30], 'loc_b': [10, 50], 'loc_c': [30, 40]}

calculated_lat = 35
calculated_lon = 40

closest_match = None
closest_distance = 10000000

for location, coordinates in json_response.items():
    distance = haversine(calculated_lat, coordinates[0], calculated_lon, 
                         coordinates[1])
    if distance < closest_distance:
        closest_match = location
        closest_distance = distance

print(closest_match)

Now you have at least a key that does exist in json_response in order to filter to the data you're interested in.

roganjosh
  • 12,594
  • 4
  • 29
  • 46
  • You're the guy I annoyed yesterday. sorry about that. I am completely open to approaching it a new way. I'll see what I can do with what you've suggested. – exmatelote Jun 28 '18 at 07:35
  • @exmatelote haha, yes, but you didn't really annoy me. But your new question confirmed my suspicion to me that I had yesterday so I'm shooting for an answer. I think you're underestimating the complexity of what you're trying to do, and this type issue business is a red herring for you. – roganjosh Jun 28 '18 at 07:38
  • I've figured out how to do it with some help from a friend. I wrote a function to do the maths and then a for loop with an if statement. There's a problem with the maths I am still working through but when I get that right i'll post what I did. – exmatelote Jul 05 '18 at 06:32
  • @exmatelote "I wrote a function to do the maths and then a for loop with an if statement." is exactly what is in my answer. – roganjosh Jul 05 '18 at 06:54