diff --git a/README.md b/README.md index 44fb8da..58db072 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,15 @@ Pushbroom Planner is a Python3 gui application that generates flight paths for aircraft carrying pushbroom scanners. ## Installation -The installation of this package requires both Python3 and pip3. Python3 comes -with Ubuntu by default, but pip3 must be installed from the package manager -via `sudo apt install python3-pip`. On Windows, Python3 and pip3 can be -downloaded and installed from [the Python website](https://www.python.org/). - +The installation of this package requires both Python3 and pip3. It's +currently targeted at Python 3.7. Python3 comes with Ubuntu by default, +but pip3 must be installed from the package manager via `sudo apt install +python3-pip`. On Windows, Python3 and pip3 can be downloaded and installed +from [the Python website](https://www.python.org/). +If using this application in production as a stand alone program, it's best +to put it in a Python virtual environement, ie: `python3.7 -m venv +ScanlineFlightPlanner` and then `. ScanFlightPlanner/bin/activate`. After installing Python3 and downloading this package, just navigate to the directory of this README and run the command `pip3 install .` A script called `scan_route_plotter` will be added in your `$PATH`, running this script will diff --git a/RoutePlotter/SHPParse.py b/RoutePlotter/SHPParse.py index 2bf868c..048c245 100644 --- a/RoutePlotter/SHPParse.py +++ b/RoutePlotter/SHPParse.py @@ -1,25 +1,29 @@ import shapefile import os -SHAPE_TYPES={'Polygon':5,'Point':1} -KEYS = 'lon','lat' +SHAPE_TYPES = {'Polygon': 5, 'Point': 1} +KEYS = 'lon', 'lat' + + def findPointCoords(shpfile): shpf = shapefile.Reader(shpfile) coords = [] for shape in shpf.shapes(): if shape.shapeType == SHAPE_TYPES['Point']: - coords.append(dict(zip(KEYS,shape.points[0]))) + coords.append(dict(zip(KEYS, shape.points[0]))) return coords + def findPolyCoords(shpfile): shpf = shapefile.Reader(shpfile) coords = [] for shape in shpf.shapes(): if shape.shapeType == SHAPE_TYPES['Polygon']: - coords+=[[dict(zip(KEYS,p))for p in shape.points]] + coords += [[dict(zip(KEYS, p)) for p in shape.points]] return coords -def findRegionType(shpfile,types_to_check=("Polygon","Point")): + +def findRegionType(shpfile, types_to_check=("Polygon", "Point")): shpf = shapefile.Reader(shpfile) for shape in shpf.shapes(): for t in types_to_check: @@ -27,82 +31,116 @@ def findRegionType(shpfile,types_to_check=("Polygon","Point")): return t return None + def findMeta(shpfile): shpf = shapefile.Reader(shpfile) keys = [key[0] for key in shpf.fields[1:]] - out = {key:[] for key in keys} + out = {key: [] for key in keys} for record in shpf.records(): - for idx,value in enumerate(record): + for idx, value in enumerate(record): out[keys[idx]].append(value) return out -def coordDictListToCoord2DList(coord_dict_list,alt=0): - coords = list(map(lambda c:[c['lon'],c['lat'],alt],coord_dict_list)) - nested_coords = [[p1,p2]for p1,p2 in zip(coords[::2],coords[1::2])] + +def coordDictListToCoord2DList(coord_dict_list, alt=0): + coords = list(map(lambda c: [c['lon'], c['lat'], alt], coord_dict_list)) + nested_coords = [[p1, p2] for p1, p2 in zip(coords[::2], coords[1::2])] return [coords] -def planOutlineFromCoords(fname,regions,alt,approach,bearing,sidelap, - inst,names,vehic='fullscale',units='US'): - polyw = shapefile.Writer(shapefile.POLYGON) - polyw.field('name','C',40) - polyw.field('alt','F',12) - polyw.field('approach','F',12) - polyw.field('bearing','F',12) - polyw.field('sidelap','F',12) - polyw.field('inst','C',40) - polyw.field('frame','S',6) - polyw.field('fov','S',8) - polyw.field('ifov','S',8) - polyw.field('pixels','F',10) - polyw.field('vehicle','C',20) - polyw.field('units','C',10) - for area,name in zip(regions,names): - bounds = coordDictListToCoord2DList(area,0) - polyw.poly(parts=bounds) +def planOutlineFromCoords(fname, regions, alt, approach, bearing, sidelap, + inst, names, vehic='fullscale', units='US', starttrig=0.5, endtrig=0.5): + polyw = shapefile.Writer(fname, shapeType=shapefile.POLYGON) + polyw.field('name', 'C', 40) + polyw.field('alt', 'F', 12) + polyw.field('approach', 'F', 12) + polyw.field('bearing', 'F', 12) + polyw.field('sidelap', 'F', 12) + polyw.field('inst', 'C', 40) + polyw.field('frame', 'S', 6) + polyw.field('fov', 'S', 8) + polyw.field('ifov', 'S', 8) + polyw.field('pixels', 'F', 10) + polyw.field('vehicle', 'C', 20) + polyw.field('units', 'C', 10) + polyw.field('trigstart', 'F', 6, 2) + polyw.field('trigend', 'F', 6, 2) + for area, name in zip(regions, names): + bounds = coordDictListToCoord2DList(area, 0) + polyw.poly(bounds) + #polyw.poly(parts=bounds) polyw.record( name, alt, approach, bearing, - sidelap*100, + sidelap * 100, inst.name, inst.frame, inst.fieldOfView, inst.crossFieldOfView, inst.pixels, vehic, - units + units, + starttrig, + endtrig ) - polyw.save(fname) - -def flightPlanFromCoords(outpath,coords,scanlinebounds,alt,speed): + #polyw.save(fname) + polyw.close() +#TODO: alt and speed do not seem to be used in writing shp files? +def flightPlanFromCoords(outpath, coords, scanlinebounds, alt, speed, trigcoords=None): if not os.path.isdir(outpath): os.makedirs(outpath) - linew = shapefile.Writer(shapefile.POLYLINE) - linew.field('idx','N',10) - footw = shapefile.Writer(shapefile.POLYGON) - footw.field('idx','N',10) - pointw = shapefile.Writer(shapefile.POINT) - pointw.field('idx','N',10) - pointw.field('type','C',40) - for i in range(int(len(coords)/4)): - #write the scan area first - bounds = coordDictListToCoord2DList(scanlinebounds[i],0) - footw.poly(parts=bounds) - footw.record(str(i),'Scanline Bounds') - #then the flight line - line=coordDictListToCoord2DList(coords[4*i:4*i+4]) - linew.poly(parts=line,shapeType=shapefile.POLYLINE) + linew = shapefile.Writer(os.path.join(outpath, 'scanlines'), shapeType=shapefile.POLYLINEZ) + linew.field('idx', 'N', 10) + footw = shapefile.Writer(os.path.join(outpath, 'footprints'), shapeType=shapefile.POLYGONZ) + footw.field('idx', 'N', 10) + pointw = shapefile.Writer(os.path.join(outpath, 'points'), shapeType=shapefile.POINTZ) + pointw.field('idx', 'N', 10) + pointw.field('type', 'C', 40) + + trigpointsw = None + triglinesw = None + if trigcoords: + trigpointsw = shapefile.Writer(os.path.join(outpath, 'trigger_points'), shapeType=shapefile.POINTZ) + triglinesw = shapefile.Writer(os.path.join(outpath, 'trigger_lines'), shapeType=shapefile.POLYLINEZ) + + trigpointsw.field('idx', 'N', 10) + trigpointsw.field('type', 'C', 40) + + triglinesw.field('idx', 'N', 10) + + for i in range(int(len(coords) / 4)): + # write the scan area first + bounds = coordDictListToCoord2DList(scanlinebounds[i], 0) + #footw.poly(parts=bounds) + footw.polyz(bounds) + footw.record(str(i), 'Scanline Bounds') + # then the flight line + line = coordDictListToCoord2DList(coords[4 * i:4 * i + 4]) + #linew.poly(parts=line, shapeType=shapefile.POLYLINE) + linew.linez(line) linew.record(i) - #then the start/end/entry/exit points of the flight line - pointw.point(*line[0][0]) - pointw.point(*line[0][1]) - pointw.point(*line[0][2]) - pointw.point(*line[0][3]) - [pointw.record(i,p)for p in["START","ENTER","EXIT","END"]] + # then the start/end/entry/exit points of the flight line + pointw.pointz(*line[0][0]) + pointw.pointz(*line[0][1]) + pointw.pointz(*line[0][2]) + pointw.pointz(*line[0][3]) + [pointw.record(i, p) for p in ["START", "ENTER", "EXIT", "END"]] + + if trigcoords: + trigline = coordDictListToCoord2DList(trigcoords[i]) + triglinesw.linez(trigline) + triglinesw.record(i) - linew.save(os.path.join(outpath,'scanlines')) - footw.save(os.path.join(outpath,'footprints')) - pointw.save(os.path.join(outpath,'points')) + trigpointsw.pointz(*trigline[0][0]) + trigpointsw.pointz(*trigline[0][1]) + for p in ["START","END"]: + trigpointsw.record(i,p) + linew.close() + footw.close() + pointw.close() + if trigcoords: + triglinesw.close() + trigpointsw.close() \ No newline at end of file diff --git a/RoutePlotter/ScanArea.py b/RoutePlotter/ScanArea.py index 6389092..56e0d3b 100644 --- a/RoutePlotter/ScanArea.py +++ b/RoutePlotter/ScanArea.py @@ -117,6 +117,8 @@ def __init__(self,home,perimeter,spectrometer=None,alt = None,bearing=None, self.setBearing(bearing) self._buildEdges() self._coords = [] + self._trigCoords = [] + self._trigBounds = [] self._alt = alt self._spectrometer = spectrometer self._name = name @@ -124,6 +126,8 @@ def __init__(self,home,perimeter,spectrometer=None,alt = None,bearing=None, self._waypoints = [] self._sidelap = 0 self._overshoot = 0 + self._trigstart = 0.5 + self._trigend = 0.5 self._find_bounds = find_scanline_bounds self._scanline_bounds = [] @@ -134,6 +138,11 @@ def setOvershoot(self,overshoot): #distance to pass beyond borders of scanarea before turning #useful for fullscale flight vehicles self._overshoot = overshoot + def setTrigStart(self, trigstart): + self._trigstart = trigstart + + def setTrigEnd(self,trigend): + self._trigend = trigend def setFindScanLineBounds(self,find_bounds): self._find_bounds = find_bounds @@ -171,6 +180,14 @@ def boundBox(self): def scanLineBoundBoxes(self): return self._scanline_bounds + @property + def triggerBoundBoxes(self): + return self._trigBounds + + @property + def trigCoords(self): + return self._trigCoords + def _computeCenter(self,perimeter): """Find the center of the set of points. This is the cartesian center rather than geographic, hopefully it doesn't make that much of a @@ -246,6 +263,20 @@ def _addScanlineBoundBox(self,scan_edge): self._scanline_bounds.append(bound_box) + def _addTrigLineBoundBox(self,trig_edge): + bound_box = [] + normal_dir = (trig_edge.bearing+90)%360 + antinormal_dir = (normal_dir+180)%360 + bound_box.append(llmath.atDistAndBearing(trig_edge.start, + self._scanline_width/18,normal_dir)) + bound_box.append(llmath.atDistAndBearing(trig_edge.start, + self._scanline_width/18,antinormal_dir)) + bound_box.append(llmath.atDistAndBearing(trig_edge.end, + self._scanline_width/18,antinormal_dir)) + bound_box.append(llmath.atDistAndBearing(trig_edge.end, + self._scanline_width/18,normal_dir)) + + self._trigBounds.append(bound_box) def _findIntersectionsInDirection(self,direction,start,curr_point=None): """Travel self._travel_width meters in direction, then scan for an intersect @@ -316,6 +347,85 @@ def _findIntersectionsInDirection(self,direction,start,curr_point=None): found_intersect = False return dir_coords[1:] + def _findTriggerIntersectionsInDirection(self, direction, start, curr_point=None): + """Travel self._travel_width meters in direction, then scan for an intersect + with an edge both parallel and antiparallel to self._leading_edge. + If no intersect is found in either direction, we've exited the ScanArea + and can return + """ + curr_point = curr_point or llmath.atDistAndBearing( + self._perimeter[0], self._travel_width, direction) + found_intersect = True + + dir_coords = [start] + trig_coords = [] + + # check to make sure we're not going to be creating too many lines + max_point = llmath.atDistAndBearing(curr_point, + self._travel_width * self.MAX_LINES, direction) + for t_dir in self._travel_dir, self._opp_dir: + for edge in self._edges: + intersect = edge.intersection(max_point, t_dir) + if intersect: + # there will be too many lines in the area, throw an error + raise ScanLineDensityError + + while found_intersect: + # don't check the leading edge + new_points = [] + for t_dir in self._travel_dir, self._opp_dir: + for edge in self._edges: + intersect = edge.intersection(curr_point, t_dir) + if intersect: + new_points.append(intersect['point']) + found_intersect = True + + curr_point = llmath.atDistAndBearing( + curr_point, self._travel_width, direction) + # append new_points to dir_coords such that the new_point closer + # to the last coord is added first + if len(new_points) > 0: + # if we cross multiple times (eg in a concave area), just take + # the two most extreme + if len(new_points) > 2: + n = len(new_points) + combos = [(i, j + 1, j - i) for i in range(n) for j in range(i + 1, n)] + new_edge = sorted([Edge(*new_points[slice(*c)]) + for c in combos], key=lambda x: x.length)[-1] + else: + new_edge = Edge(*new_points) + + new_points = new_edge.endpoints + #if self._find_bounds: + # self._addScanlineBoundBox(new_edge) + + # if we have an overshoot, stick 2 additional points beyond + # the extrema + trig_points = [] + if self._overshoot > 0: + tg1 = llmath.atDistAndBearing(new_points[0], + self._overshoot * self._trigstart, (180 + new_edge.bearing) % 360) + tg2 = llmath.atDistAndBearing(new_points[1], + self._overshoot * self._trigend, new_edge.bearing) + new_points = [tg1, *new_points, tg2] + trig_points = [tg1,tg2] + else: + trig_points = [new_points[0],new_points[1]] + if self._find_bounds: + self._addTrigLineBoundBox(Edge(*trig_points)) + + + dists_from_coords = new_edge.distanceTo(dir_coords[-1]) + if dists_from_coords[0] > dists_from_coords[1]: + dir_coords += new_points[::-1] + trig_coords.append(trig_points[::-1]) + else: + dir_coords += new_points + trig_coords.append(trig_points) + else: + found_intersect = False + return trig_coords + def findScanLines(self): self._scanline_bounds = [] self._coords = [self._home] @@ -340,12 +450,16 @@ def findScanLines(self): self._coords += self._findIntersectionsInDirection( parallel_dir1,self._perimeter[2],first_point)[::first_traverse] + self._trigCoords += self._findTriggerIntersectionsInDirection( + parallel_dir1, self._perimeter[2], first_point)[::first_traverse] if first_point is not None: first_point = llmath.atDistAndBearing( self._center,self._travel_width,parallel_dir2) self._coords += self._findIntersectionsInDirection( parallel_dir2,self._coords[-1],first_point) + self._trigCoords += self._findTriggerIntersectionsInDirection( + parallel_dir2, self._coords[-1], first_point) #flip things around so that we start by travelling the shorter #route from home at the beginning @@ -354,6 +468,7 @@ def findScanLines(self): if end_dists[0] > end_dists[1]: print(len(self._coords),end=' ') self._coords = [self._home]+self._coords[1:][::-1] + self._trigCoords = self._trigCoords[::-1] print(len(self._coords)) return self._coords @@ -441,6 +556,9 @@ def __init__(self,home,spectrometer=None,alt = None,bearing=None, self._sidelap = 0 self._coords = None self._overshoot = overshoot + self._trigstart = 0.5 + self._trigend = 0.5 + self._trigCoords = [] self._vehicle = 'quadcopter' self._names = names self._find_bounds = find_scanline_bounds @@ -481,6 +599,16 @@ def setOvershoot(self,overshoot): for sa in self.scanAreas: sa.setOvershoot(self._overshoot) + def setTrigStart(self, trigstart): + self._trigstart = trigstart + for sa in self.scanAreas: + sa.setTrigStart(self._trigstart) + + def setTrigEnd(self, trigend): + self._trigend = trigend + for sa in self.scanAreas: + sa.setTrigEnd(self._trigend) + def setSidelap(self,sidelap): self._sidelap = sidelap for sa in self.scanAreas: @@ -540,6 +668,13 @@ def flattenCoords(self): #and returns to home at the end return flat_coords + def flattenTrigCoords(self): + flat_coords = [] + for coords in self._trigCoords: + flat_coords += coords + + return flat_coords + def findScanLines(self): """Find the scan lines of each ScanArea, then chain them together""" self._coords = [] @@ -549,6 +684,7 @@ def findScanLines(self): sa.setHome(home) new_coords = sa.findScanLines()[1:] self._coords.append(new_coords) + self._trigCoords.append(sa.trigCoords) home = self._coords[-1][-1] if self._vehicle == 'quadcopter': @@ -581,6 +717,17 @@ def scanLineBoundBoxes(self): boxes += sa.scanLineBoundBoxes return boxes + @property + def triggerBoundBoxes(self): + boxes = [] + for sa in self._scanareas: + boxes += sa.triggerBoundBoxes + return boxes + + @property + def triggerCoords(self): + return self._trigCoords + def plot(self,show=True): for sa in self.scanAreas[:-1]: sa.plot(show=False,include=['perimeter','bounds']) @@ -606,7 +753,7 @@ def toShapeFile(self,fname): self.findScanLines() speed = self._spectrometer.squareScanSpeedAt(self._alt) SHPParse.flightPlanFromCoords(fname, - self.flattenCoords(),self.scanLineBoundBoxes,self._alt,speed) + self.flattenCoords(), self.scanLineBoundBoxes, self._alt, speed, self.flattenTrigCoords()) def toProjectShapeFile(self,fname,units): """ @@ -620,7 +767,7 @@ def toProjectShapeFile(self,fname,units): print(self._spectrometer._name) SHPParse.planOutlineFromCoords(fname,perims,self._alt,self._overshoot, self._bearing,self._sidelap,self._spectrometer,names, - self._vehicle,units) + self._vehicle,units,self._trigstart,self._trigend) @classmethod def fromProjectShapeFile(Cls,shp_fname,home=None): diff --git a/RoutePlotter/gui.py b/RoutePlotter/gui.py index f5aa442..99507ff 100644 --- a/RoutePlotter/gui.py +++ b/RoutePlotter/gui.py @@ -93,7 +93,9 @@ def __init__(self): self._region = None self._spectrometer = None self._vehicle="fullscale" - self._overshoot = 30 + self._overshoot = 30 + self._trigstart = 0.5 + self._trigend = 0.5 self._sidelap = .2 self._names = [] self.p = None @@ -110,6 +112,14 @@ def setOvershoot(self,val): if(self._isfloat(val)): self._overshoot = max(1,float(val)) + def setTrigStart(self,val): + if(self._isfloat(val)): + self._trigstart = float(val)/100.0 + + def setTrigEnd(self,val): + if(self._isfloat(val)): + self._trigend = float(val)/100.0 + def setSidelap(self,val): if(self._isfloat(val)): self._sidelap = float(val)/100. @@ -177,13 +187,19 @@ def finishLoad(self,fname): self._bearing = meta['bearing'][0] self._sidelap = meta['sidelap'][0] self._overshoot = meta['approach'][0] + try: + self._trigstart = float(meta['trigstart'][0]) + self._trigend = float(meta['trigend'][0]) + except: + self._trigstart = 0.5 + self._trigend = 0.5 #print(self._alt,self._bearing,self._sidelap,self._overshoot) self._spectrometer = spectrometer self.js_callback.Call(coords,self._vehicle,self._alt,self._bearing, self._sidelap*100,self._overshoot, spectrometer.fieldOfView, spectrometer.crossFieldOfView,spectrometer._px, - spectrometer._name,meta['name'] + spectrometer._name,meta['name'],self._trigstart,self._trigend ) def polygonizePoints(self,points,js_callback): @@ -207,6 +223,8 @@ def createPath(self,coords,js_callback,err_callback): region.setBearing(self._bearing) region.setSidelap(self._sidelap) region.setOvershoot(self._overshoot) + region.setTrigStart(self._trigstart) + region.setTrigEnd(self._trigend) region.setFindScanLineBounds(True) scanner = self._spectrometer or Spectrometer.HeadwallNanoHyperspec() @@ -216,12 +234,14 @@ def createPath(self,coords,js_callback,err_callback): try: region.findScanLines() coords = region.flattenCoords() + trigCoords = region.flattenTrigCoords() bounds= region.boundBox scanlines=region.scanLineBoundBoxes + trigBoxes = region.triggerBoundBoxes dist = "%.2f"%(region.totalScanLength/1000) speed = "%.2f"%region.scanVelocity self._region = region - js_callback.Call(coords,bounds,dist,speed,px_size,scanlines) + js_callback.Call(coords,bounds,dist,speed,px_size,scanlines,trigCoords,trigBoxes) except ScanArea.ScanLineDensityError: err_callback.Call() diff --git a/RoutePlotter/gui/index.html b/RoutePlotter/gui/index.html index 9f3e1d8..abb6802 100644 --- a/RoutePlotter/gui/index.html +++ b/RoutePlotter/gui/index.html @@ -104,6 +104,16 @@
+ Trigger Start specifies where along the approach to + trigger the spectrometer to start. 100% starts the spectrometer as soon as approach + begins, and 0% starts at the exact edge of the scan area. +
++ Trigger Stop specifies where along the approach to + trigger the spectrometer to stop. 100% starts the spectrometer as the end of the exit approach + end, and 0% stops at the exact edge of the scan area. +
Bearing is the angle at which scan lines will pass through the scan area. It is given relative to North. @@ -375,11 +385,19 @@
| Altitude (ft): | -+ | |
| Approach (mi): | -+ | + |
| Trigger Start (%): | ++ | |
| Trigger End (%): | +||
| Bearing (°): | @@ -390,6 +408,9 @@
| Distance (mi): | diff --git a/RoutePlotter/gui/js/map.js b/RoutePlotter/gui/js/map.js index d371aa6..251299c 100644 --- a/RoutePlotter/gui/js/map.js +++ b/RoutePlotter/gui/js/map.js @@ -312,14 +312,20 @@ var setHomeMarker = function(latlng){ var scanLines=[]; var scanLineBounds=[]; +var trigLines=[]; +var trigLinesBounds=[]; var scanPath; -var setAirplaneScanPath = function(latlngs,scanlines){ + + +var setAirplaneScanPath = function(latlngs,scanlines,triggLines,triggBounds){ if(scanPath) scanPath.setMap(null); if(homeMarker) homeMarker.setMap(null); if(scanLines.length > 0){ _.each(scanLines,(line)=>line.setMap(null)); _.each(scanLineBounds,(box)=>box.setMap(null)); + _.each(trigLines,(line)=>line.setMap(null)); + _.each(trigLinesBounds,(box)=>box.setMap(null)); }; _.each(_.groupBy(latlngs,(ll,idx)=>Math.floor(idx/4)),function(lls,idx){ @@ -333,13 +339,31 @@ var setAirplaneScanPath = function(latlngs,scanlines){ zIndex:999 })); scanLineBounds.push(new google.maps.Polygon({ - paths:_.map(scanlines[idx],(c)=>cleanPyCoords(c)), - strokeColor:'#0000ff', - strokeOpacity:0, - strokeWeight:0, - fillColor: '#4B0082', - fillOpacity: 0.40, - map:map + paths:_.map(scanlines[idx],(c)=>cleanPyCoords(c)), + strokeColor:'#0000ff', + strokeOpacity:0, + strokeWeight:0, + fillColor: '#4B0082', + fillOpacity: 0.40, + map:map, + })); + trigLines.push(new google.maps.Polyline({ + path:_.map(triggLines[idx],(c)=>cleanPyCoords(c)), + geodesic:true, + strokeColor: '#00FF00', + strokeOpacity:1.0, + strokeWeight: 4, + map:map, + zIndex:999 + })); + trigLinesBounds.push(new google.maps.Polygon({ + paths:_.map(triggBounds[idx],(c)=>cleanPyCoords(c)), + strokeColor:'#0000ff', + strokeOpacity:0, + strokeWeight:0, + fillColor: '#00CC82', + fillOpacity: 0.20, + map: map })); }); @@ -442,9 +466,10 @@ var setScanSpeed=function(speed){ } }; -var createPathCallback= function(coords,bounds,dist,speed,pxsize,scanlines){ +var createPathCallback= function(coords,bounds,dist,speed,pxsize,scanlines,trigCoords,trigBoxes){ coords = _.map(coords,cleanPyCoords); bounds = _.map(bounds,cleanPyCoords); + $('#scan_passes').html(scanlines.length); if(UNITS=='US'){ $('#scan_len').html(Math.round(km2mi(dist))); $('#px_size').html(m2ft(pxsize).toFixed(2)); @@ -454,7 +479,7 @@ var createPathCallback= function(coords,bounds,dist,speed,pxsize,scanlines){ } setScanSpeed(speed); if($('#vehicle').val()=='fullscale') - setAirplaneScanPath(coords,scanlines); + setAirplaneScanPath(coords,scanlines,trigCoords,trigBoxes); else setScanPath(coords); @@ -474,7 +499,7 @@ var createPathFailedCallback = function(){ } var loadFileCallback = function(coords,vehicle,alt,bearing,sidelap, - overshoot,fov,ifov,px,s_name,p_names){ + overshoot,fov,ifov,px,s_name,p_names,trigstart,trigend){ if(UNITS=='US'){ alt = m2ft(alt); overshoot = km2mi(overshoot/1000); @@ -492,6 +517,8 @@ var loadFileCallback = function(coords,vehicle,alt,bearing,sidelap, $('#ifov').val(ifov); $('#px').val(px); $('#spectrometer').val(s_name); + $('#trigstart').val(trigstart*100); + $('#trigend').val(trigend*100); _.each(coords,function(perim,idx){ perim = _.map(perim,cleanPyCoords); userDrawnRegion.addPolyFromCoords(perim,p_names[idx]); @@ -593,6 +620,14 @@ $(document).ready(function(){ external.setOvershoot(1000*$(this).val()); generatePath(); }); + $('#trigstart').change(function(){ + external.setTrigStart($(this).val()); + generatePath(); + }); + $('#trigend').change(function(){ + external.setTrigEnd($(this).val()); + generatePath(); + }); $('#sidelap').change(function(){ external.setSidelap($(this).val()); generatePath(); diff --git a/setup.py b/setup.py index 6dc0596..98eb70d 100644 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from distutils.core import setup setup(name='RoutePlotter', - version='1.0', + version='1.2', description='Scanning spectrometer flight-route calculator with gui', author='Matthew Westphall', author_email='w.matthew.he@gmail.com', @@ -11,9 +11,9 @@ package_data={'': ['**/**/*.js','**/**/*.html','**/**/*.css','**/**/*.png']}, scripts=['scan_route_plotter'], install_requires=[ - 'pyshp', - 'cefpython3', - 'lxml', - 'numpy' + 'pyshp==2.1.0', + 'cefpython3==66.0', + 'lxml==4.5.2', + 'numpy==1.19.0' ], )