├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── README.md
├── build_uav_toolkit.pro
├── custom_code
├── README.md
├── bandAlignment
│ ├── bandalignment_applyreferencetiepointstogroups.pro
│ ├── bandalignment_applyreferencetiepointswithenvi.pro
│ ├── bandalignment_applyreferencetiepointswithidl.pro
│ ├── bandalignment_find_good_image_group.pro
│ ├── bandalignment_generatereferencetiepoints.pro
│ ├── bandalignment_get_image_groups.pro
│ ├── bandalignment_group_to_virtualraster.pro
│ ├── bandalignment_processsensor_getreferencetiepoints.pro
│ ├── bandalignment_savereferencetiepoints.pro
│ ├── bandalignment_set_task_parameters.pro
│ ├── bandalignment_setupsensorforprocessing.pro
│ ├── bandalignment_simple_kappa_filter.pro
│ ├── bandalignmenttiepoints__define.pro
│ ├── uavbandalignment.pro
│ ├── uavbandalignment.task
│ └── uavbandalignmentbasic.task
├── batchTasks
│ ├── uavbatchrededge.pro
│ └── uavbatchrededge.task
└── filterRaster
│ └── filter_raster.pro
├── docs
├── .nojekyll
├── CHANGELOG.html
├── CONTRIBUTING.html
├── LICENSE.html
├── css
│ ├── anchor-fix.css
│ ├── bootstrap.min.css
│ ├── callouts.css
│ ├── copy-button.css
│ ├── cosmos.bootstrap.min.css
│ ├── figures-and-tables.css
│ ├── general-styles.css
│ ├── haroopad-markdown.css
│ ├── idl-styles.css
│ ├── search-results.css
│ └── sidebar.css
├── custom_code
│ ├── README.html
│ ├── bandAlignment
│ │ └── uavbandalignment.html
│ └── batchTasks
│ │ └── uavbatchrededge.html
├── images
│ ├── L3Harrislogowhite.svg
│ ├── baseline_code_white_48dp.png
│ ├── baseline_home_white_48dp.png
│ ├── baseline_menu_white_48dp.png
│ └── baseline_search_white_48dp.png
├── index.html
├── index
│ ├── index.html
│ ├── indexSearch.js
│ └── lunrIndex.js
├── js
│ ├── LICENSE.clipboard.txt
│ ├── LICENSE.lunr.txt
│ ├── bootstrap.min.js
│ ├── clipboard.min.js
│ ├── copy-button.js
│ ├── highlight
│ │ ├── LICENSE
│ │ ├── highlight.pack.js
│ │ └── styles
│ │ │ ├── agate.css
│ │ │ ├── atom-one-dark-reasonable.css
│ │ │ ├── atom-one-dark.css
│ │ │ ├── atom-one-light.css
│ │ │ ├── github-gist.css
│ │ │ ├── github.css
│ │ │ ├── monokai-sublime.css
│ │ │ ├── railscasts.css
│ │ │ ├── rainbow.css
│ │ │ └── solarized-dark.css
│ ├── jquery.min.js
│ └── lunr.min.js
├── src
│ ├── generate_cam_file.html
│ ├── generate_gps_file.html
│ ├── join_rededge_datadirs.html
│ ├── rededge_to_radiance.html
│ └── unjoin_rededge_datadir.html
└── uav_toolkit.html
├── idl.lint.json
├── idl.package.json
├── idl.package.lock.json
├── idl.test.json
├── idl_packages
├── github-bridgeit
│ ├── LICENSE.txt
│ ├── README.md
│ ├── bridge_it__define.pro
│ └── bridge_it_example.pro
└── github-se-awesomeenvitoolkit
│ ├── .gitignore
│ ├── LICENSE.txt
│ ├── README.md
│ ├── idl.lint.json
│ ├── idl.package.json
│ ├── idl.package.lock.json
│ ├── idl.test.json
│ └── src
│ ├── awesomeENVIProgress
│ ├── README.md
│ ├── awesomeenviprogress__define.pro
│ └── awesomeenviprogress__define.spec.pro
│ ├── awesomeENVITask
│ ├── awesomeenvitask.pro
│ ├── awesomeenvitask.spec.pro
│ └── classify_raster.task
│ ├── awesomeGetENVI
│ ├── awesomegetenvi.pro
│ └── awesomegetenvi.spec.pro
│ ├── awesomeSelectTaskParameters
│ └── awesomeselecttaskparameters.pro
│ ├── awesomeTileIterator
│ ├── README.md
│ ├── createawesomeoptimizedtileiterator.pro
│ ├── createawesometileiterator.pro
│ └── createawesometileiterator.spec.pro
│ ├── awesomecatchblock.pro
│ ├── awesomeenviareextensionsinitialized.pro
│ ├── awesomepixelsize.pro
│ ├── awesomexmlparse.pro
│ ├── bak
│ └── awesomeenvirastergetdata.pro.bak
│ └── enviraster_methods
│ ├── enviraster__awesomeexport.pro
│ └── enviraster__awesomeexport.spec.pro
├── src
├── generate_cam_file.pro
├── generate_gps_file.pro
├── get_co_calibration.pro
├── get_histograms.pro
├── get_reflectance_panels.pro
├── image_groups_to_virtual_rasters.pro
├── image_info__define.pro
├── join_rededge_datadirs.pro
├── rededge_to_radiance.pro
├── uav_toolkit_calibrate_data.pro
└── unjoin_rededge_datadir.pro
├── tests
├── uav_toolkit_test_calibration.spec.pro
├── uav_toolkit_test_generic.spec.pro
├── uav_toolkit_test_generic_multipage.spec.pro
├── uav_toolkit_test_rededge.spec.pro
├── uav_toolkit_test_rededge_panel_extraction.spec.pro
├── uav_toolkit_test_rededge_radiance.spec.pro
└── uav_toolkit_test_sequoia.spec.pro
└── uav_toolkit.pro
/.gitignore:
--------------------------------------------------------------------------------
1 | # .gitignore file initialized by the IDL Developer Tools
2 | /dist
3 | *spec.pro.log
4 | *idl.test.log
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | This file summarizes the overall changes for the UAV toolkit.
4 |
5 | ## Version 2.3.5
6 |
7 | Updated copyright statements
8 |
9 | Added updated example to README for UAVBatchRededge
10 |
11 | Rebuilt docs
12 |
13 | Some minor updates to the idl.package.json file
14 |
15 | ## Version 2.3.1
16 |
17 | Comment updates to add tooltips
18 |
19 | Documentation updated
20 |
21 | LICENSE.txt updated to add "Copyright"
22 |
23 | idl.package.json updated for hooks into ENVI Tasks and Extensions
24 |
25 | ## Version 2.3
26 |
27 | Updated the reflectance panel extractor to work better when the background is also bright. As long as the reflectance panels are greater than the mean reflectance of the image, they should be extractable. The new algorithm finds a measure of "squareness" and uses an optmization function to extract the most "square" clump of pixels in the scene.
28 |
29 | Documentation has been updated.
30 |
31 |
32 | ## Version 2.2
33 |
34 | Updated some of the progress messages when running ENVI headlessly
35 |
36 | Added support for data that is already a multi-page TIFF in a single file, created a test for it, and updated the README with an example.
37 |
38 |
39 | ## Version 2.1
40 |
41 | Added buttons to the uav_toolkit procedure so that you can have a UI for the tools and updated the readme. With this change, the progress information has also been updated.
42 |
43 | Separated code that belogs o other repositories which is now included in the `idl_pacakges` folder.
44 |
45 | Fixed a bug with Parrot Sequoia metadata have a different tag name for ISOSpeed.
46 |
47 | Updated some routine names, task display names, and reorganized task parameters. This should not affect any code as the proper entry point is through the tasks.
48 |
49 | Added a new Basic version of the UAV Toolkit Band Alignment task that has about 1/3 of the parameters to make it easier to use in the ENVI UI.
50 |
51 |
52 | ## Version 2.0.2
53 |
54 | When generating the tie points for the bands, we algorithm now only uses ENVI's advanced algorithm for tie point generation. This means that the tie point generation process may take 10-15 minutes, depending on your data. This change removed the `RIGOROUS_ALIGNMENT` task parameter of the `UAVBandAlignment` task.
55 |
56 |
57 | ## Version 2.0.1
58 |
59 | There have been many overall updates to the UAV Toolkit since it's initial release which consists of:
60 |
61 | - Complete automation of the process for generating and applying reference tie points to imagery.
62 |
63 | There is a new routine that works behind the scenes to find an image group that the reference tie points can be taken from and it has worked very well on the test datasets that I have used.
64 |
65 | - Refactoring and optimization of much of the code
66 |
67 | Behind the scenes a new suite of routines is used for performing the band-band registration that can be used with any type of imagery or raster.
68 |
69 | - Generic support for any multispectral sensor that produces separate images for each band.
70 |
71 | If there are other sensors feel free to make a request for adding them, provided sample data can be shared.
72 |
73 | - Refocused overall tools for band-band alignment
74 |
75 | - **This means that some routines have been removed completely from this version of the UAV Toolkit. If there are missing functions/procedures that you want which are missing, then file a bug report via the main GitHub repository and I will add them back in.**
76 |
77 | - Packaged the tools to use the ENVI Task framework
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | See the [ENVI contributing guide](https://github.com/envi-idl/ContribGuide) and the [IDL contributing guide](https://github.com/interactive-data-language/ContribGuide) for code formatting information.
4 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2018 Harris Geospatial Solutions, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.TRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/build_uav_toolkit.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 |
8 | ;+
9 | ;
10 | ; :Private:
11 | ;
12 | ; IDL batch file that will compile the code present into a single IDL
13 | ; SAVE file called the UAV Toolkit. This will also place the task files next to
14 | ; the compiled source code.
15 | ;
16 | ; This is only necessary if you are trying to use the UAV Toolkit without IDL
17 | ; or with the enterprise version of ENVI/IDL.
18 | ;
19 | ; Just press compile + run in the IDL workbench to build the IDL SAVE file.
20 | ;
21 | ; The output directory will be called "UAVToolkit-build" in your system temporary
22 | ; folder. This helps avoid any conflicts on IDL's search path.
23 | ;
24 | ;
25 | ; :Author: Zachary Norman - GitLab: [znorman-harris](https://github.com/znorman-harris)
26 | ;-
27 |
28 |
29 | ;get this source directory
30 | thisDir = file_dirname(routine_filepath())
31 |
32 | ;set up our output location
33 | outDir = filepath('', /TMP) + 'UAVToolkit-build'
34 | if ~file_test(outDir, /DIRECTORY) then file_mkdir, outDir
35 |
36 | ;search for PRO files
37 | files = file_search(thisDir, '*.pro', COUNT = nPro)
38 |
39 | ;validate
40 | if (nPro eq 0) then begin
41 | message, 'No PRO files found in this source directory, where did they go?'
42 | endif
43 |
44 | ;array of files to exclude form the build
45 | exclude = ['build_uav_toolkit.pro']
46 |
47 | ;start child process
48 | bdg = idl_idlbridge()
49 |
50 | ;compile each file
51 | foreach file, files do begin
52 | ;skip condition
53 | if (total(strlowcase(file_basename(file)) eq exclude) gt 0) then continue
54 | bdg.execute, '.compile "' + file + '"'
55 | endforeach
56 |
57 | ;resolve dependencies
58 | bdg.execute, 'resolve_routine, "readexif__define"'
59 | bdg.execute, 'resolve_routine, "exifmetadata__define"'
60 | bdg.execute, 'resolve_all, /CONTINUE_ON_ERROR, SKIP_ROUTINES = ["envi", "envi_doit"]'
61 |
62 | ;save routines and clean up
63 | bdg.execute, 'save, /ROUTINES, FILENAME = "' + outDir + path_sep() + 'uav_toolkit.sav"'
64 | obj_destroy, bdg
65 |
66 | ;copy task files
67 | taskFiles = file_search(thisDir, '*.task', COUNT = nTask)
68 | if (nTask gt 0) then file_copy, taskFiles, outDir, /OVERWRITE
69 |
70 | ;alert user of output location
71 | print, 'Build located at : ' + outDir
72 | end
73 |
--------------------------------------------------------------------------------
/custom_code/README.md:
--------------------------------------------------------------------------------
1 | # UAVToolkit Directory custom_code
2 |
3 | This folder contains the source code used for the different tasks in the UAV Toolkit. They are separated by the overall tasks that they belong to. The task files are also going to be located in these subfolders.
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_applyreferencetiepointswithenvi.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; TODO: save grid definition for regridding iamges so that it can be used elsewhere
14 | ;
15 | ; :Description:
16 | ; Procedure that uses built-in ENVI tasks to perform the image-image
17 | ; registration for band alignment. Instead of doing things in memory, this
18 | ; routine creates files on disk based on the `INPUT_REGISTRATION_TASK` that
19 | ; is specified by the user.
20 | ;
21 | ; Once finished, this task then tiles over the stacked layers and turns off
22 | ; pixels that do not have valid data values for each band. This helps remove
23 | ; potential artifacts around the edges of images that might appear.
24 | ;
25 | ; Fot the stacking this task uses virtual rasters behind the scene which
26 | ; will help make this process more efficient.
27 | ;
28 | ;
29 | ; :Keywords:
30 | ; INPUT_RASTER: in, required, type=ENVIRaster
31 | ; Specify the raster that you want to apply the `INPUT_TIEPOINTS` to.
32 | ; INPUT_TIEPOINTS: in, required, type=BandAlignmentTiePoints
33 | ; This should be set to a pointer array that contains the file coordinates
34 | ; for the tiepoints that will be applied to the images.
35 | ; IMAGE_REGISTRATION_TASK: in, requried, type=ENVITask, taskname=ImageToImageRegistration
36 | ; Provide the task that you want to be used for image-image registration when
37 | ; lining the bands up to one another.
38 | ; OUTPUT_RASTER_URI: in, optional, type=string
39 | ; Specifyt he fully-qualified output file that will be written to disk with the
40 | ; bands registered to one another.
41 | ;
42 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
43 | ;-
44 | pro BandAlignment_ApplyReferenceTiePointsWithENVI,$
45 | INPUT_RASTER = input_raster,$
46 | INPUT_BANDALIGNMENTTIEPOINTS = input_BandAlignmentTiePoints,$
47 | IMAGE_REGISTRATION_TASK = image_registration_task,$
48 | OUTPUT_RASTER_URI = output_raster_uri
49 | compile_opt idl2
50 |
51 | e = envi(/current)
52 | if (e eq !NULL) then begin
53 | e = envi(/headless)
54 | endif
55 |
56 | ;get info on our raster
57 | nBands = input_raster.NBANDS
58 |
59 | ;make sure that our tie points are valid
60 | if ~isa(input_BandAlignmentTiePoints, 'BandAlignmentTiePoints') then begin
61 | message, 'INPUT_BANDALIGNMENTTIEPOINTS is not a valid BandAlignmentTiePoints object, requried!'
62 | endif
63 |
64 | ;extract the tie points from our object
65 | use_tiepoints = input_BandAlignmentTiePoints.TIE_POINTS
66 |
67 | ;set our reference to the first invalid tiepoint set
68 | idxRef = where(~ptr_valid(use_tiepoints), countRef)
69 | if (countRef eq 0) then begin
70 | message, 'No reference band found within the INPUT_TIEPOINTS and all pointers are valid. Were they generated correctly?'
71 | endif
72 | reference_band = idxRef[0]
73 |
74 | ;create a lost of rasters to clean up
75 | clean = list()
76 |
77 | ;get base band
78 | baseRaster = ENVISubsetRaster(input_raster, BANDS = reference_band)
79 | clean.add, baseRaster
80 |
81 | ;preallocate an array to hold our rasters
82 | outputRasters = objarr(input_raster.NBANDS)
83 |
84 | ;save our base raster
85 | outputRasters[reference_band] = baseRaster
86 |
87 | ;iterate over each tiepoint task
88 | for i=0,nBands-1 do begin
89 | ;skip our base band
90 | if (i eq reference_band) then continue
91 |
92 | ;subset our raster
93 | bandRaster = ENVISubsetRaster(input_raster, BANDS = i)
94 |
95 | ;make a tiepointset
96 | tiePoints = ENVITiePointSet(TIEPOINTS = *use_tiepoints[i], $
97 | INPUT_RASTER1 = baseRaster, INPUT_RASTER2 = bandRaster)
98 |
99 | ;duplicate our registration task
100 | regTask = ENVITask('ImageToImageRegistration')
101 | regTask.INPUT_TIEPOINTS = tiePoints
102 | regTask.WARPING = image_registration_task.WARPING
103 | regTask.POLYNOMIAL_DEGREE = image_registration_task.POLYNOMIAL_DEGREE
104 | regTask.RESAMPLING = image_registration_task.RESAMPLING
105 | regTask.BACKGROUND = image_registration_task. BACKGROUND
106 | regTask.FULL_EXTENT = image_registration_task.FULL_EXTENT
107 | regTask.OUTPUT_PIXEL_SIZE = image_registration_task.OUTPUT_PIXEL_SIZE
108 |
109 | ;run the task
110 | regTask.execute
111 |
112 | ;save output raster and add to data collection
113 | outputRasters[i] = regTask.OUTPUT_RASTER
114 | clean.add, regTask.OUTPUT_RASTER
115 |
116 | ;close our band raster if not our reference band
117 | bandRaster.close
118 | endfor
119 |
120 | ;find the grid that covers the intersection of all of our data
121 | foreach raster, outputRasters, i do begin
122 | ;skip reference
123 | if (i eq reference_band) then continue
124 |
125 | ; Create a grid definition for the first raster
126 | Raster1CoordSys = ENVICoordSys(COORD_SYS_STR = baseRaster.SPATIALREF.COORD_SYS_STR)
127 | Raster1Grid = ENVIGridDefinition(Raster1CoordSys, $
128 | PIXEL_SIZE=baseRaster.SPATIALREF.PIXEL_SIZE, $
129 | NROWS=baseRaster.NROWS, $
130 | NCOLUMNS=baseRaster.NCOLUMNS, $
131 | TIE_POINT_MAP=baseRaster.SPATIALREF.TIE_POINT_MAP, $
132 | TIE_POINT_PIXEL=baseRaster.SPATIALREF.TIE_POINT_PIXEL)
133 |
134 | ; Create a grid definition for the second raster
135 | Raster2CoordSys = ENVICoordSys(COORD_SYS_STR = raster.SPATIALREF.COORD_SYS_STR)
136 | Raster2Grid = ENVIGridDefinition(Raster2CoordSys, $
137 | PIXEL_SIZE=raster.SPATIALREF.PIXEL_SIZE, $
138 | NROWS=raster.NROWS, $
139 | NCOLUMNS=raster.NCOLUMNS, $
140 | TIE_POINT_MAP=raster.SPATIALREF.TIE_POINT_MAP, $
141 | TIE_POINT_PIXEL=raster.SPATIALREF.TIE_POINT_PIXEL)
142 |
143 | ; Get the intersection of the two rasters
144 | grid_definition = ENVIGridDefinition(Raster1CoordSys, $
145 | EXTENT = Raster1Grid.Intersection(Raster2Grid), $
146 | PIXEL_SIZE=baseRaster.SPATIALREF.PIXEL_SIZE < raster.SPATIALREF.PIXEL_SIZE)
147 |
148 | ;regrid our reference
149 | baseRaster = ENVISpatialgridRaster(baseRaster, GRID_DEFINITION = grid_definition)
150 | clean.add, baseRaster
151 | endforeach
152 |
153 | ;regrid all of our rasters
154 | foreach raster, outputRasters, i do $
155 | outputRasters[i] = ENVISpatialgridRaster(raster, GRID_DEFINITION = grid_definition)
156 | clean.add, outputRasters, /EXTRACT
157 |
158 | ;stack together
159 | stackedRaster = ENVIMetaspectralRaster(outputRasters, $
160 | SPATIALREF = outputRasters[reference_band].SPATIALREF)
161 | clean.add, stackedRaster
162 |
163 | ;check if we want to write to disk
164 | if keyword_set(output_raster_uri) then begin
165 | ;init raster after updating metadata to have data ignore value
166 | meta = stackedRaster.METADATA
167 | meta['data ignore value'] = 0
168 | output_raster = ENVIRaster(INHERITS_FROM = stackedRaster, URI = output_raster_uri, METADAT = meta)
169 |
170 | ;create tile iterator
171 | createAwesomeTileIterator,$
172 | INPUT_RASTER = stackedRaster,$
173 | OUTPUT_SUB_RECTS = sub_rects
174 |
175 | ;tile over our raster
176 | foreach sub, sub_rects do begin
177 | ;read raster data
178 | dat = stackedRaster.getData(SUB_RECT = sub, PIXEL_STATE = ps, INTERLEAVE = 'BSQ')
179 |
180 | ;collapse pixel state
181 | ps = total(ps, 3, /INTEGER)
182 |
183 | ;find pixels to turn off
184 | idxOff = where(ps, countOff)
185 | if (countOff gt 0) then begin
186 | for z=0, nBands-1 do begin
187 | bandDat = dat[*,*,z]
188 | bandDat[idxOff] = 0
189 | dat[*,*,z] = temporary(bandDat)
190 | endfor
191 | endif
192 |
193 | ;write our data
194 | output_raster.setData, dat, SUB_RECT = sub
195 | endforeach
196 |
197 | ;finalize raster
198 | output_raster.save
199 | clean.add, output_raster
200 | endif
201 |
202 | ;clean up
203 | foreach r, clean do if isa(r, 'ENVIRaster') then r.close
204 | end
205 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_applyreferencetiepointswithidl.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Procedure that performs the image-image registration in memory using
15 | ; IDL and an RST warping method. It returns the output spatial reference
16 | ; and a pointer contianing the data which can be used to create an output
17 | ; raster.
18 | ;
19 | ; For example, after running the procedure you can create a raster like this:
20 | ;
21 | ; ;register our bands using IDL
22 | ; BandAlignment_ApplyReferenceTiePointsWithIDL,$
23 | ; INPUT_RASTER = input_raster,$
24 | ; INPUT_BANDALIGNMENTTIEPOINTS = filtered_tiepoints,$
25 | ; REFERENCE_BAND = reference_band,$
26 | ; OUTPUT_SPATIALREF = output_spatialref,$
27 | ; OUTPUT_DATA_POINTER = output_data_pointer
28 | ;
29 | ; ;save as an ENVI raster
30 | ; newRaster = ENVIRaster(*output_data_pointer, SPATIALREF = output_spatialref)
31 | ; newRaster.save
32 | ;
33 | ; The purpose of providing a pointer is to reduce memory costs with larger scenes and to give
34 | ; users the options to save the data as any desired format such as TIFF, JPEG, PNG, or ENVI.
35 | ;
36 | ;
37 | ; :Keywords:
38 | ; INPUT_RASTER: in, required, type=ENVIRaster
39 | ; Specify the input raster that you want to perform band-band alignment on.
40 | ; INPUT_BANDALIGNMENTTIEPOINTS: in, required, type=BandAlignmentTiePoints
41 | ; Specify the BandAlignmentTiePoints object that contains the tie points generated from
42 | ; the procedure ```idl BandAlignment_GenerateReferenceTiePoints```.
43 | ; OUTPUT_SPATIALREF, out, required, type=ENVISpatialReference
44 | ; Contains the output spatial reference for the band-band aligned datasets.
45 | ; OUTPUT_DATA_POINTER: out, requried, type=pointer
46 | ; This will contain a pointer that references the data that is ready to
47 | ; be written to disk. The pointer can be accessed with the asterisk to
48 | ; extract the data. i.e.:
49 | ;
50 | ; dat = *output_data_pointer
51 | ;
52 | ; This pointer is used to minimize the amount of memory that is consumed.
53 | ;
54 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
55 | ;-
56 | pro BandAlignment_ApplyReferenceTiePointsWithIDL,$
57 | BAND_DATA_POINTERS = band_data_pointers,$
58 | INPUT_RASTER = input_raster,$
59 | INPUT_BANDALIGNMENTTIEPOINTS = input_BandAlignmentTiePoints,$
60 | OUTPUT_SPATIALREF = output_spatialref,$
61 | OUTPUT_DATA_POINTER = output_data_pointer
62 | compile_opt idl2, hidden
63 |
64 | ;validate our input
65 | if ~isa(input_BandAlignmentTiePoints, 'BandAlignmentTiePoints') then begin
66 | message, 'INPUT_BANDALIGNMENTTIEPOINTS is not a valid BandAlignmentTiePoints object!'
67 | endif
68 | if ~isa(input_raster, 'ENVIRASTER') then begin
69 | message, 'INPUT_RASTER is not a valid ENVIRASTER object!'
70 | endif
71 |
72 | ;get number of bands
73 | nBands = input_raster.NBANDS
74 | nPtrs = n_elements(band_data_pointers)
75 |
76 | ;make sure that we have RST, otherwise calculate it
77 | if ~input_BandAlignmentTiePoints.RST_CALCULATED then begin
78 | input_BandAlignmentTiePoints.GenerateRSTTransform, input_raster
79 | endif
80 |
81 | ;extract information from our tie points
82 | dims = input_BandAlignmentTiePoints.RST_OUTPUT_DIMENSIONS
83 | rst = input_BandAlignmentTiePoints.RST_TRANSFORMS
84 | subs = input_BandAlignmentTiePoints.RST_SUB_RECT
85 |
86 | ;check if we were already provided with our data
87 | if (nPtrs eq nBands) then begin
88 | ;preallocate an array to hold our data for warping
89 | ;get the typecode from our input raster by reading a single pixel
90 | write_data = make_array(dims[0], dims[1], nbands, $
91 | TYPE = ((*band_data_pointers[0])[0]).TYPECODE, /NOZERO)
92 | endif else begin
93 | ;preallocate an array to hold our data for warping
94 | ;get the typecode from our input raster by reading a single pixel
95 | write_data = make_array(dims[0], dims[1], nbands, $
96 | TYPE = (input_raster.GetData(BANDS = 0, SUB_RECT = [0,0,1,1])).TYPECODE, /NOZERO)
97 | endelse
98 |
99 | ;register all our bands!
100 | for i=0,nbands-1 do begin
101 | ;get the RST transform
102 | xy1 = *rst[i]
103 |
104 | ;fill our data array with values
105 | ;check if we were already provided with our data
106 | if (nPtrs eq nBands) then begin
107 | write_data[*,*,i] = interpolate(*band_data_pointers[i], xy1[0,*], xy1[1,*])
108 | endif else begin
109 | write_data[*,*,i] = interpolate(input_raster.GetData(BANDS = i), xy1[0,*], xy1[1,*])
110 | endelse
111 | endfor
112 |
113 | ;create a pointer to our image data
114 | output_data_pointer = ptr_new(write_data, /NO_COPY)
115 |
116 | ;make a spatial reference for our data - this get's within 1/5 of a pixel and
117 | ;is good enough
118 | subset = ENVISubsetRaster(input_raster, SUB_RECT = [subs[0],subs[2],subs[1], subs[3]])
119 |
120 | ;get our spatialreference use ENVIHydrate so that we have an
121 | ;object that does not below to the virtual raster
122 | ;this keeps it alive when we close the virtual raster
123 | output_spatialref = ENVIHydrate(subset.SPATIALREF.dehydrate())
124 |
125 | ;close our subset
126 | subset.close
127 | end
128 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_find_good_image_group.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Procedure that will automatically find the best image group to
15 | ; use for the band-band registration for a given set of data. This
16 | ; routine is data-set independent and just relies on the `GROUPS`
17 | ; keyword below.
18 | ;
19 | ;
20 | ;
21 | ; :Keywords:
22 | ; GROUPS: input, required, type=hash/orderedhash
23 | ; Set this input keyword to an ordered hash where the key/value
24 | ; pairs correspond to the folder and base name of the image groups and an array of
25 | ; the fully-qualified paths to the iamges for that group. For example, here is
26 | ; an image group for the MicaSense RedEdge:
27 | ;
28 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289":[
29 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289_1.tif",
30 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289_2.tif",
31 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289_3.tif",
32 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289_4.tif",
33 | ; "C:\\Users\\Traininglead\\Desktop\\test\\north_block_east_west\\000\\IMG_0289_5.tif"
34 | ; ]
35 | ; INPUTDIR: in, optional, type=string
36 | ; This optional keyword can be set to the directory that wants to be searched for
37 | ; image groups. You can use this if you don't pass in the `GROUPS` keyword. This
38 | ; assumes that there are only MicaSense RedEdge images present.
39 | ; OUTPUT_IMAGE_GROUP: out, requried, type=string
40 | ; The key name for the image group that is the most likely candidate for providing
41 | ; good band-band registration results.
42 | ; KAPPA_FILTER: in, optional, type=boolean
43 | ; This optional keyword will filter out scenes that may be associated with fixed-wing
44 | ; drones turning around. You don't want to use these images for generating tiepoints
45 | ; because they do not have the same viewing geometry as the nadir images.
46 | ; BASEBAND: in, optional, type=int, default=0
47 | ; This optional keyword specifies the zero-based band number that you want to use for
48 | ; evaluating the quality of the band for reference tiepoints.
49 | ;
50 | ; :Author: Zachary Norman - znorman@harris.com
51 | ;-
52 | pro bandalignment_find_good_image_group, $
53 | GROUPS = groups, $
54 | OUTPUT_IMAGE_GROUP = output_image_group,$
55 | KAPPA_FILTER = kappa_filter,$
56 | BASEBAND = baseband
57 | compile_opt idl2, hidden
58 |
59 | ;check to see if ENVI is running
60 | e = awesomeGetENVI()
61 |
62 | ;check what the baseband is
63 | ;default is first
64 | if (baseband eq !NULL) then begin
65 | baseband = 0
66 | endif
67 |
68 | ;make sure we have groups
69 | if ~n_elements(groups) then begin
70 | message, 'No groups passed in for processing, required!', LEVEL = -1
71 | endif
72 |
73 | ;initialize progress
74 | prog = awesomeENVIProgress('Finding Ideal Image Group', /PRINT)
75 | prog.setProgress, 'Processing...', 0, /PRINT
76 |
77 | ;get the group names that fall within approximate straight lines if asked for
78 | if keyword_set(kappa_filter) then begin
79 | ;filter groups - error thrown internally if no groups exist after filtering
80 | groupNames = bandalignment_simple_kappa_filter(groups)
81 | endif else begin
82 | groupNames = (groups.keys()).toArray()
83 | endelse
84 |
85 | ;get the number of groups
86 | nGroups = n_elements(groupNames)
87 |
88 | ;determine how often to print
89 | nPrint = (nGroups lt 100) ? 1 : floor(nGroups/100.0)
90 |
91 | ;preallocate an array to hold our metric for the best image to register
92 | edges = dblarr(nGroups)
93 |
94 | ;alert user
95 | prog.setProgress, 'Processing', 0, /PRINT
96 |
97 | ;iterate over each image group
98 | foreach groupName, groupNames, i do begin
99 | ;get the file names for each group
100 | images = groups[groupName]
101 |
102 | ;open raster and get data from first band
103 | ;TODO: maybe improve this logic if bands are in one file?
104 | rasters = e.openRaster(images[baseband])
105 | dat = rasters[0].getdata(BANDS = [0])
106 | foreach r, rasters do r.close
107 |
108 | ;check if we were cancelled
109 | prog.abortRequested
110 |
111 | ;find the total number of edges in the image
112 | edges[i] = mean(sobel(temporary(dat)))
113 |
114 | ;update user
115 | if ~(i mod nPrint) then prog.setProgress, 'Processing...', 100*float(i+1)/nGroups, /PRINT, /TIME
116 | endforeach
117 |
118 | ;get the min/max values
119 | maxEdge = max(edges, idx_max)
120 | minEdge = min(edges, idx_min)
121 |
122 | ;return the iamge with the largest mean value of edges
123 | output_image_group = groupNames[idx_max]
124 |
125 | ;finish our progress
126 | prog.finish, /PRINT
127 | end
128 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_get_image_groups.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; This function searches a directory for files and groups multiple files together
15 | ; based on unique identifiers. Essentially, this function is useful for grouping
16 | ; images together that represent different bands for a dataset if each band is in
17 | ; a separate file.
18 | ;
19 | ; This routine is also helpful to re-order the bands by increasing/decreasing wavelengths
20 | ; as is demonstrated in the example below.
21 | ;
22 | ; :Example:
23 | ; To search for MicaSense RedEdge files you can simply use the following (5 comes before 4):
24 | ;
25 | ; dir = 'C:\some\directory\000'
26 | ; ids = ['_1.tif', '_2.tif', '_3.tif', '_5.tif', '_4.tif']
27 | ; groups = BandAlignment_Get_Image_Groups(dir, ids)
28 | ;
29 | ; This same routine can be used to search for other data such as Parrot Sequoia:
30 | ;
31 | ; dir = 'C:\some\directory\data'
32 | ; ids = ['_GRE.TIF', '_RED.TIF', '_NIR.TIF', '_REG.TIF']
33 | ; groups = BandAlignment_Get_Image_Groups(dir, ids)
34 | ;
35 | ; :Params:
36 | ; directory: in, required, type=string
37 | ; Specify the directory that you want to search for imagery
38 | ; uniqueIdentifiers: in, requried, type=stringarr
39 | ; Specify the unique parts of each filename that correspond to the identifier
40 | ; for that file. This should be something like the end of the file. See the
41 | ; examples above for a reference.
42 | ;
43 | ;
44 | ;
45 | ; :Author: Zachary Norman - Github: [znorman-harris](https://github.com/znorman-harris)
46 | ;-
47 | function BandAlignment_Get_Image_Groups, directory, uniqueIdentifiers
48 | compile_opt idl2, hidden
49 |
50 | ;get the number of bands
51 | nBands = n_elements(uniqueIdentifiers)
52 |
53 | ;save search results
54 | files = orderedhash()
55 |
56 | ;init data structure to store the group information
57 | groups = orderedhash()
58 |
59 | ;find files in the directory
60 | cd, directory, CURRENT = first_dir
61 | dirFiles = file_search(COUNT = nFiles)
62 | cd, first_dir
63 |
64 | ;return empty orderedhash
65 | if (nFiles eq 0) then return, groups
66 |
67 | ;search for files
68 | foreach id, uniqueIdentifiers, i do begin
69 | idxMatch = where(strpos(strlowcase(dirFiles), strlowcase(id)) ne -1, countMatch)
70 |
71 | ;make sure we found results
72 | if (countMatch eq 0) then return, groups
73 |
74 | ;save complete files
75 | files[id] = directory + path_sep() + dirFiles[idxMatch]
76 |
77 | ;save base names of first matching ID (any works for finding all other groups)
78 | if (i eq 0) then baseNames = file_basename(strlowcase(dirFiles[idxMatch]), strlowcase(id))
79 | endforeach
80 |
81 | ;find the matches for each base name
82 | foreach base, baseNames do begin
83 | ;init list to store matches
84 | matches = list()
85 |
86 | ;loop over each list of files
87 | foreach fileArr, files do begin
88 | idx = where(strpos(strlowcase(fileArr), base) ne -1, countIdx)
89 | if (countIdx eq 1) then begin
90 | matches.Add, fileArr[idx[0]]
91 | endif
92 | endforeach
93 |
94 | ;validate that we have anough matches
95 | if (n_elements(matches) eq nBands) then begin
96 | groups[base] = matches.toArray()
97 | endif else begin
98 | print, ' incomplete group "' + base + '", skipping...'
99 | endelse
100 | endforeach
101 |
102 | ;return group information
103 | return, groups
104 | end
105 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_group_to_virtualraster.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Function that creates a virtual raster from the input image groups. The
15 | ; raster has a dummy spatial reference applied so that the image can be
16 | ; processed by image-registration routines.
17 | ;
18 | ; Note that each file must have the same dimensions.
19 | ;
20 | ; :Returns:
21 | ; A virtual raster where each band represents one of the images in `groupFiles`.
22 | ;
23 | ; :Params:
24 | ; groupFiles: in, required, type=string[*]
25 | ; Specify the files that represent an image group.
26 | ;
27 | ;
28 | ;
29 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
30 | ;-
31 | function bandAlignment_group_to_virtualRaster, groupFiles, RASTERS = rasters
32 | compile_opt idl2, hidden
33 |
34 | e = envi(/current)
35 |
36 | ;open as rasters
37 | rasters = list()
38 | foreach file, groupFiles do rasters.Add, e.openRaster(file), /EXTRACT
39 |
40 | ;check for spatialref and make dummy one if not present
41 | if (rasters[0]).SPATIALREF eq !NULL then begin
42 | spatialref = ENVIStandardRasterSpatialRef($
43 | coord_sys_code = 4326, $
44 | /GEOGCS,$
45 | pixel_size = [0.00001d, 0.00001d], $
46 | tie_point_pixel = [0, 0], $
47 | tie_point_map = [0, 0])
48 | endif else begin
49 | spatialref = (rasters[0]).SPATIALREF
50 | endelse
51 |
52 | ;make a metaspectral raster
53 | return, ENVIMetaspectralRaster(rasters.toArray(), SPATIALREF = spatialref)
54 | end
55 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_processsensor_getreferencetiepoints.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Simple procedure that will generate tie points base don the input group.
15 | ;
16 | ; :Params:
17 | ; group: in, requried, type=string[*]
18 | ; Array of strigns that represents the files to process.
19 | ; parameters: in, required, type=dictionary
20 | ; The dictionary parameters used for all the band alignment processing.
21 | ;
22 | ; :Keywords:
23 | ; RASTER: in, optional, type=ENVIRASTER
24 | ; If set, then we wont stack the images into a raster. Can save a small amount of time.
25 | ; NO_OUTPUT: in, optional, type=boolean, default=false
26 | ; If set, then no output files will be created. Otherwise a .dat file and tie points
27 | ; will be saved to disk. Used for secondary image registration.
28 | ; FILTERED_TIEPOINTS: out, optional, type=bandAlignmentTiePoints
29 | ; Returns a direct reference to the tie points that are generated by this procedure.
30 | ;
31 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
32 | ;-
33 | pro BandAlignment_ProcessSensor_GetReferenceTiePoints, group, parameters, $
34 | RASTER = raster, $
35 | NO_OUTPUT = no_output, $
36 | FILTERED_TIEPOINTS = filtered_tiepoints
37 | compile_opt idl2, hidden
38 | e = envi(/CURRENT)
39 |
40 | ;initialize progress
41 | prog = awesomeENVIProgress('Generating Reference Tie Points', /PRINT)
42 | prog.setProgress, 'Initializing', 0, /PRINT
43 |
44 | ;check if we were provided a raster or not
45 | if ~isa(raster, 'ENVIRASTER') then begin
46 | cleanup = 1
47 | raster = bandAlignment_group_to_virtualRaster(group, RASTERS = sourceRasters)
48 | endif
49 |
50 | ;set task parameters - from banadalignment_set_task_parameters which ALWAYS gets called
51 | ;first so we should have the function defined
52 |
53 | ;set custom task params based on sensor
54 | case (parameters.SENSOR) of
55 | 'rededge':bandalignment_set_rededge_task_parameters, parameters.CORRELATION_TASK, parameters, group
56 | else:;do nothing
57 | endcase
58 |
59 | ;generate reference tiepoints
60 | BandAlignment_GenerateReferenceTiepoints,$
61 | INPUT_RASTER = raster,$
62 | PROGRESS = prog,$
63 | TIEPOINT_GENERATION_TASK = ~(parameters.RIGOROUS_ALIGNMENT) ? parameters.CORRELATION_TASK : parameters.MUTUAL_TASK,$
64 | TIEPOINT_FILTERING_TASK = parameters.FILTER_TASK,$
65 | REFERENCE_BAND = parameters.BASE_BAND,$
66 | MINIMUM_FILTERED_TIEPOINTS = parameters.MINIMUM_FILTERED_TIEPOINTS,$
67 | OUTPUT_BANDALIGNMENTTIEPOINTS = filtered_tiepoints
68 |
69 | ;create output if not told to skip
70 | if ~keyword_set(no_output) then begin
71 | prog.setProgress, 'Generating output', 99, /PRINT
72 |
73 | ;save the tie points
74 | BandAlignment_SaveReferenceTiepoints,$
75 | INPUT_BANDALIGNMENTTIEPOINTS = filtered_tiepoints,$
76 | OUTPUT_TIEPOINTS_SAVEFILE_URI = parameters.POINTS_FILE
77 |
78 | ;create image that we can look at
79 | BandAlignment_ApplyReferenceTiePointsWithIDL,$
80 | INPUT_RASTER = raster,$
81 | INPUT_BANDALIGNMENTTIEPOINTS = filtered_tiepoints,$
82 | OUTPUT_DATA_POINTER = datPtr,$
83 | OUTPUT_SPATIALREF = outSref
84 |
85 | ;save to disk
86 | if ~file_test(parameters.RIGOROUS_DIR + '_out') then file_mkdir, parameters.RIGOROUS_DIR + '_out'
87 | uri = parameters.RIGOROUS_DIR + '_out' + path_sep() + file_basename(group[0], '.tif') + '.dat'
88 | if file_test(uri) then file_delete, uri, /QUIET
89 | if ~file_test(uri) then begin
90 | if parameters.hasKey('METADATA') then begin
91 | newRaster = ENVIRaster(*datPtr, SPATIALREF = outSref, URI = uri, METADATA = parameters.METADATA)
92 | endif else begin
93 | newRaster = ENVIRaster(*datPtr, SPATIALREF = outSref, URI = uri)
94 | endelse
95 | newRaster.save
96 | newRaster.close
97 | endif else begin
98 | print, 'Unable to create preview with alignment using reference tiepoints, file locked. File:'
99 | print, ' ' + uri
100 | endelse
101 |
102 | ;clean up so we dont have locks on any files
103 | raster.close
104 | foreach file, group do begin
105 | rasters = e.openRaster(file)
106 | foreach r, rasters do if isa(r, 'enviraster') then r.close
107 | endforeach
108 | endif
109 |
110 | ;check if we need to clean up our source rasters
111 | if keyword_set(cleanup) then begin
112 | if isa(raster, 'ENVIRaster') then raster.close
113 | foreach r, sourceRasters do if isa(r, 'ENVIRaster') then r.close
114 | endif
115 |
116 | ;finish our progress
117 | prog.finish, /PRINT
118 | end
119 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_savereferencetiepoints.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Simple wrapper procedure that validates and saves our tie points to an
15 | ; IDL save file for later use. The tie points that are saved represent
16 | ; an array of pointers that, if valid pointers, contain the file coordinates
17 | ; of two rasters for image registration.
18 | ;
19 | ; To use the tie points later you will need to do the following:
20 | ;
21 | ; file = 'C:\someeDir\points.sav'
22 | ; tiePoints = BandAlignmentTiePoints(SAVE_FILE = file)
23 | ;
24 | ;
25 | ;
26 | ; :Keywords:
27 | ; INPUT_BANDALIGNMENTTIEPOINTS: in, required, type=BandAlignmentTiePoints
28 | ; Specify the BandAlignmentTiePoints object that contains the file coordinates for
29 | ; our ENVI tie points.
30 | ; OUTPUT_TIEPOINTS_SAVEFILE_URI: in, optional, type=string
31 | ; Specify the fully-qualified path to an IDL SAVE file that will contain the
32 | ; tie points. If not specified, then this keyword will be populated with
33 | ; a filename that was picked by ENVI.
34 | ;
35 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
36 | ;-
37 | pro BandAlignment_SaveReferenceTiepoints,$
38 | INPUT_BANDALIGNMENTTIEPOINTS = input_BandAlignmentTiePoints,$
39 | OUTPUT_TIEPOINTS_SAVEFILE_URI = output_tiepoints_savefile_uri
40 | compile_opt idl2, hidden
41 |
42 | ;get current ENVI
43 | e = envi(/CURRENT)
44 |
45 | ;make sure that our outputs are specified
46 | if ~keyword_set(output_tiepoints_savefile_uri) then begin
47 | output_tiepoints_savefile_uri = e.getTemporaryFilename('sav')
48 | endif
49 |
50 | ;make sure we have valid tiepoints
51 | if ~isa(input_BandAlignmentTiePoints, 'BandAlignmentTiePoints') then begin
52 | message, 'input_tiepoints are not a valid BandAlignmentTiePoints object, required!'
53 | endif
54 |
55 | ;duplicate variable name for consistent naming
56 | bandAlignmentTiePointsObject = input_BandAlignmentTiePoints
57 |
58 | ;save to disk
59 | save, bandAlignmentTiePointsObject, FILENAME = output_tiepoints_savefile_uri
60 | end
61 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_set_task_parameters.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; Simple routine that sets task parameters from the input parameters. Just convenience
14 | ; in case we need to set parameters in multiple places.
15 | ;
16 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
17 | ;-
18 | pro bandalignment_set_rededge_task_parameters, task, parameters, group
19 | compile_opt idl2, hidden
20 |
21 | ;check if we want a dynamic search window
22 | if parameters.SEARCH_WINDOW_FROM_HEIGHT then begin
23 | ;extract image properties
24 | oImageInfo = obj_new('image_info', group[0], /PRINT_GPS)
25 | height = oImageInfo.Get('HEIGHT_ABOVE_GROUND')
26 |
27 | ;update the task
28 | task.SEARCH_WINDOW = 2*(((height>0)/3 < 100) + 5)
29 | endif else begin
30 | ;update the task
31 | task.SEARCH_WINDOW = parameters.SEARCH_WINDOW
32 | endelse
33 |
34 | ;set matching window
35 | task.MATCHING_WINDOW = (task.SEARCH_WINDOW/2) > 21
36 | end
37 |
38 |
39 |
40 | pro bandalignment_set_task_parameters, parameters
41 | compile_opt idl2, hidden
42 |
43 | ;set cross correlation parameters
44 | parameters.CORRELATION_TASK.MINIMUM_MATCHING_SCORE = parameters.CORRELATION_MATCHING_SCORE
45 | parameters.CORRELATION_TASK.REQUESTED_NUMBER_OF_TIEPOINTS = parameters.REQUESTED_NUMBER_OF_TIEPOINTS
46 | parameters.CORRELATION_TASK.SEARCH_WINDOW = parameters.CORRELATION_SEARCH_WINDOW
47 | parameters.CORRELATION_TASK.MATCHING_WINDOW = (parameters.CORRELATION_TASK.SEARCH_WINDOW/2) > 21
48 |
49 | ;set mutual information parameters
50 | parameters.MUTUAL_TASK.MINIMUM_MATCHING_SCORE = parameters.MUTUAL_MATCHING_SCORE
51 | parameters.MUTUAL_TASK.REQUESTED_NUMBER_OF_TIEPOINTS = parameters.REQUESTED_NUMBER_OF_TIEPOINTS
52 | parameters.MUTUAL_TASK.SEARCH_WINDOW = parameters.MUTUAL_SEARCH_WINDOW
53 | parameters.MUTUAL_TASK.MATCHING_WINDOW = (parameters.MUTUAL_TASK.SEARCH_WINDOW/2) > 21
54 |
55 | end
56 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_setupsensorforprocessing.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Main orchestrator for processing data for band-band alignment. This will
15 | ; set up tasks, task parameters, and call the right routines for generating
16 | ; and applying reference tie points.
17 | ;
18 | ; There is a case statement at the beginning of the procedure that contians the
19 | ; defaults for each sensor type.
20 | ;
21 | ;
22 | ;
23 | ; :Keywords:
24 | ; PARAMETERS: in, required, type=dictionary
25 | ; Pass in the parameters needed for image-iamge registration. A
26 | ; dictionary is used for simplicity and convenience to be able to
27 | ; easily add new parameters for use in different routines.
28 | ;
29 | ; :Author: Zachary Norman - GitLab: [znorman-harris](https://github.com/znorman-harris)
30 | ;-
31 | pro BandAlignment_SetUpSensorForProcessing, PARAMETERS = parameters
32 | compile_opt idl2, hidden
33 |
34 | ;get current ENVI
35 | e = awesomeGetENVI()
36 |
37 | ;search for image groups
38 | groups = bandalignment_get_image_groups(parameters.INPUTDIR, parameters.FILE_IDENTIFIERS)
39 | if (n_elements(groups) eq 0) then begin
40 | message, 'No image groups found for processing in directory "' + parameters.INPUTDIR + '"'
41 | endif
42 |
43 | ;initialize our tasks for image-image registration
44 | ;generic
45 | correlationTask = ENVITask('GenerateTiePointsByCrossCorrelation')
46 | correlationTask.MINIMUM_MATCHING_SCORE = parameters.CORRELATION_MATCHING_SCORE
47 | correlationTask.REQUESTED_NUMBER_OF_TIEPOINTS = parameters.REQUESTED_NUMBER_OF_TIEPOINTS
48 | parameters['CORRELATION_TASK'] = correlationTask
49 |
50 | ;rigorous
51 | mutualTask = ENVITask('GenerateTiePointsByMutualInformation')
52 | mutualTask.MINIMUM_MATCHING_SCORE = parameters.MUTUAL_MATCHING_SCORE
53 | mutualTask.REQUESTED_NUMBER_OF_TIEPOINTS = parameters.REQUESTED_NUMBER_OF_TIEPOINTS
54 | parameters['MUTUAL_TASK'] = mutualTask
55 |
56 | ;update overall task parameters based on our input parameters
57 | bandalignment_set_task_parameters, parameters
58 |
59 | ;tie point filtering task
60 | filterTask = ENVITask('FilterTiePointsByGlobalTransform')
61 | parameters['FILTER_TASK'] = filterTask
62 |
63 | ;check what we need to do for our processing
64 | if keyword_set(parameters.GENERATE_REFERENCE_TIEPOINTS) then begin
65 | ;lets try and find a good image group to use for generating reference tie points
66 | bandalignment_find_good_image_group, $
67 | GROUPS = groups, $
68 | OUTPUT_IMAGE_GROUP = reference_image_group,$
69 | KAPPA_FILTER = parameters.KAPPA_FILTER,$
70 | BASEBAND = parameters.BASE_BAND
71 |
72 | ;because we have a good group, write to folder called rigorous
73 | rigDir = file_dirname(parameters.INPUTDIR) + path_sep() + 'rigorous'
74 | parameters['RIGOROUS_DIR'] = rigDir
75 | if file_test(rigDir) then begin
76 | file_delete, rigDir, /RECURSIVE, /QUIET
77 | wait, 0.01 ;permissions issues sometimes if we dont wait, not sure why
78 | endif
79 |
80 | ;copy new files to the right directory so that users can see
81 | ;which images are picked
82 | if ~file_test(rigDir) then file_mkdir, rigDir
83 | file_copy, groups[reference_image_group], rigDir, /OVERWRITE
84 |
85 | ;generate the reference tie points
86 | BandAlignment_ProcessSensor_GetReferenceTiePoints,$
87 | groups[reference_image_group], parameters
88 | endif
89 |
90 | ;check if we want to apply our reference tiepoints
91 | if keyword_set(parameters.APPLY_REFERENCE_TIEPOINTS) then begin
92 | ;make sure that we have reference tie points to use
93 | if ~file_test(parameters.POINTS_FILE) then begin
94 | message, 'Requested to apply reference tiepoints, but no reference tie points found! Please generate first.'
95 | endif
96 |
97 | ;check if we need to perform localized histogram color balancing
98 | if keyword_set(parameters.CO_CALIBRATION) then begin
99 | ;check if we have reference information about reflectance panels
100 | ;if so, get the means and camera exposure settings
101 | if file_test(parameters.PANELDIR, /DIRECTORY) then begin
102 | ;search for image group sin our panel directory
103 | panelGroups = bandalignment_get_image_groups(parameters.PANELDIR, parameters.FILE_IDENTIFIERS)
104 |
105 | ;only process if we find images
106 | if (n_elements(panelGroups) gt 0) then begin
107 | ;get the keys for our groups
108 | keys = panelGroups.keys()
109 |
110 | ;check if we have max valules to set
111 | maxFlag = parameters.hasKey('MAX_PIXEL_VALUE') AND parameters.hasKey('MAX_VALUE_DIVISOR')
112 |
113 | ;attempt to extract the reflectance panels
114 | panel_info = get_reflectance_panels(panelGroups[keys[0]], parameters.SENSOR,$
115 | MAX_PIXEL_VALUE = maxFlag ? parameters.MAX_PIXEL_VALUE : !NULL,$
116 | MAX_VALUE_DIVISOR = maxFlag ? parameters.MAX_VALUE_DIVISOR : !NULL,$
117 | PANEL_REFLECTANCE = parameters.PANEL_REFLECTANCE)
118 |
119 | ;save the panel information
120 | save, panel_info, FILENAME = parameters.PANELDIR + path_sep() + 'panel_info.sav'
121 | endif
122 | endif else begin
123 | panel_info = dictionary()
124 | endelse
125 |
126 | ;extract scale factors for each group
127 | get_co_calibration, $
128 | GROUPS = groups,$
129 | PANEL_INFO = panel_info
130 | endif
131 |
132 | ;apply our reference tiepoints to our image groups
133 | BandAlignment_ApplyReferenceTiePointsToGroups, groups, parameters
134 | endif
135 | end
136 |
--------------------------------------------------------------------------------
/custom_code/bandAlignment/bandalignment_simple_kappa_filter.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ;
10 | ;-
11 |
12 | ;+
13 | ; :Description:
14 | ; Routine that returns key names for the image groups that likely fall
15 | ; within a straight line. This can remove images from the candidate pool
16 | ; from points where a fixed wing UAV is turning. Helps ensure better quality
17 | ; image groups are found for generating reference tiepoints.
18 | ;
19 | ; :Params:
20 | ; groups: in, required, type=hash/orderedhash
21 | ; The image groups that you want to apply kappa filtering
22 | ; for.
23 | ;
24 | ;
25 | ; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
26 | ;-
27 | function bandalignment_simple_kappa_filter, groups, DEBUG = debug
28 | compile_opt idl2
29 | if ~keyword_set(debug) then on_error, 2
30 |
31 | ;=======================================
32 | ;hard coded parameters
33 | ;number of left-right images to check
34 | ncheck = 2
35 |
36 | ;threshold for maximum allowable degree separation for straight lines
37 | theta_diff = 5 ;degrees
38 | theta_diff *= ncheck
39 | ;=======================================
40 |
41 | ;get the names of our groups
42 | groupNames = groups.keys()
43 | ngroups = n_elements(groups)
44 |
45 | ;preallocate an array to hold positions
46 | locations = dblarr(2,ngroups) ;[lon, lat]
47 |
48 | ;get all iamge locations
49 | i=0
50 | foreach images, groups, group do begin
51 | oInfo = obj_new('image_info', images[0], /NO_SPATIALREF, /NO_PRINT, /ERROR_GPS)
52 | locations[*,i] = oInfo.Get('LON_LAT')
53 | i++
54 | endforeach
55 |
56 | ;preallocate ana rray to hold our kappa angles
57 | kappas = dblarr(ngroups-ncheck)
58 |
59 | ;iterate over each group for kappa
60 | for i=ncheck, ngroups-1-ncheck do begin
61 | ;preallocate arrays
62 | inFront = 0.0
63 | inBack = 0.0
64 |
65 | ;find all angle differences
66 | for j=1,ncheck do begin
67 | inFront += atan((locations[1,i+j] - locations[1,i]), (locations[0,i+j] - locations[0,i]))*(1/(!DTOR))
68 | inBack += atan((locations[1,i] - locations[1,i-j]), (locations[0,i] - locations[0,i-j]))*(1/(!DTOR))
69 | endfor
70 |
71 | ;save value
72 | kappas[i] = inFront - inBack
73 | endfor
74 |
75 | ;get stats
76 | meanval = mean(kappas)
77 | stddev = stddev(kappas)
78 |
79 | ;find which groups we can use
80 | available = where((abs(kappas) lt theta_diff) AND ([0:ngroups-1-ncheck] gt (ncheck-1)), count_valid)
81 |
82 | ;make sure that we found some results
83 | if (count_valid eq 0) then begin
84 | message, 'Flight lines are not straight enough to filter out turning points in the flight lines!' + $
85 | string(10b) + string(9b) + ' No valid images found to check for automating tie point generation process.', LEVEL = -1
86 | endif
87 |
88 | ;debug plots to show before/after filtering
89 | if keyword_set(debug) then begin
90 | sp1 = scatterplot(locations[0,*],locations[1,*])
91 | sp2 = scatterplot(locations[0,available],locations[1,available], $
92 | SYM_COLOR = 'red', /OVERPLOT, current = SP1)
93 | end
94 |
95 | ;return our best group
96 | return, groupNames[available]
97 | end
98 |
--------------------------------------------------------------------------------
/custom_code/batchTasks/uavbatchrededge.task:
--------------------------------------------------------------------------------
1 | {
2 | "name": "UAVBatchRedEdge",
3 | "version": "5.3",
4 | "baseClass": "ENVITaskFromProcedure",
5 | "routine": "uavbatchrededge",
6 | "displayName": "UAV Batch RedEdge",
7 | "description": "Task that batch processes a folder of MicaSense RedEdge imagery.",
8 | "parameters": [
9 | {
10 | "name": "BAND_ALIGNMENT_TASK",
11 | "displayName": "Band Alignment Task",
12 | "description": "Specify a UAVBandAlignment task that you want to use for processing the RedEdge data. A default task will be used if one is not provided.",
13 | "direction": "input",
14 | "parameterType": "optional",
15 | "dataType": "ENVITASK"
16 | },
17 | {
18 | "name": "FLIGHTDIR",
19 | "displayName": "Flightdir",
20 | "description": "Specify the folder that contains directories of RedEdge data that will be batch processed. All flights must have the same height above the ground for the alignment to be valid.",
21 | "direction": "input",
22 | "parameterType": "required",
23 | "dataType": "string"
24 | },
25 | {
26 | "name": "PANEL_REFLECTANCE",
27 | "displayName": "Panel Reflectance",
28 | "description": "This represents the percent reflectance (0 to 100) of reflectance panel images that are provided in PANELDIR. Specify a single value or an array of values that represents the percent reflectance of each band of the reflectance panel. The value should be between 0 and 100. If a scalar is provided, then it is assumed to be a constant value for each band. The order of this array should match the FILE_IDENTIFIERS. If you are using Sequioa or RedEdge data, then it is from shortest to longest wavelength.",
29 | "direction": "input",
30 | "parameterType": "required",
31 | "dataType": "float[*]",
32 | "min": 0,
33 | "max": 100,
34 | "defaultValue":[70]
35 | }
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/custom_code/filterRaster/filter_raster.pro:
--------------------------------------------------------------------------------
1 | ;h+
2 | ; Copyright (c) 2019 Harris Geospatial Solutions, Inc.
3 | ;
4 | ; Licensed under MIT. See LICENSE.txt for additional details and information.
5 | ;h-
6 |
7 | ;+
8 | ; :Private:
9 | ; Routine to apply sobel filter to raster to try and improve band-band registration
10 | ;
11 | ;
12 | ; :Keywords:
13 | ; INPUT_RASTER
14 | ; FILTER
15 | ; OUTPUT_RASTER_URI
16 | ;
17 | ; :Author: znorman
18 | ;-
19 | pro filter_raster,$
20 | INPUT_RASTER = input_raster,$
21 | FILTER = filter,$
22 | OUTPUT_RASTER_URI = output_raster_uri
23 | compile_opt idl2
24 |
25 | e = envi(/current)
26 | if (e eq !NULL) then begin
27 | e = envi(/headless)
28 | endif
29 |
30 | if (filter eq !NULL) then begin
31 | filter = 'roberts'
32 | endif
33 |
34 | ;make sure the output filename is specified
35 | if (output_raster_uri eq !NULL) then begin
36 | output_raster_uri = e.GettemporaryFilename()
37 | endif
38 |
39 | ;get info on our raster
40 | dims = [input_raster.NCOLUMNS, input_raster.NROWS]
41 | nbands = input_raster.NBANDS
42 |
43 | ;initialize our tile iterator
44 | ;add a buffer of 3 to each tile since our kernels are mostly 3 x 3 (i think)
45 | createAwesomeTileIterator,$
46 | INPUT_RASTER = input_raster,$
47 | TILE_BUFFER = 3, $
48 | TILE_SIZE = [20480, 20480],$
49 | NCOLUMNS = ncolumns,$
50 | NROWS = nrows,$
51 | OUTPUT_SUB_RECTS = output_sub_rects,$
52 | OUTPUT_RASTER_TILE_LOCATIONS = output_raster_tile_locations,$
53 | OUTPUT_TILE_SUB_RECTS = output_tile_sub_rects
54 |
55 |
56 | ;get original raster metadata
57 | orig_meta = input_raster.meta.dehydrate()
58 |
59 | ;set up our output raster
60 | meta = ENVIRasterMetadata()
61 | meta.AddItem, 'band names', filter.CapWords() + ' Filter: ' + orig_meta['BAND NAMES']
62 |
63 | ;tile!
64 | foreach sub_rect, output_sub_rects, i do begin
65 | ;get the data
66 | tile_data = input_raster.GetData(SUB_RECT = sub_rect, INTERLEAVE = 'BSQ', PIXEL_STATE = ps)
67 |
68 | ;update any nans
69 | idxBad = where(~finite(tile_data), countBad, COMPLEMENT = idxGood, NCOMPLEMENT = countGood)
70 | if (countBad gt 0) then begin
71 | if (countGood eq 0) then begin
72 | message, 'No valid data to process....'
73 | endif
74 |
75 | stop
76 |
77 | ;get tile size
78 | tDims = size(tile_data, /DIMENSIONS)
79 |
80 | ; ps[idxBad]++
81 | ; tile_data[idxBad] = fix(interpolate(tile_data[idxGood], idxGood mod tDims[0], idxGood / tDims[0], $
82 | ; idxBad mod tDims[0], idxBad / tDims[1]), TYPE=tile_data.TYPECODE)
83 | endif
84 |
85 | ;do edge detection for every band
86 | ;ensure that the type of the data matches the original
87 | ;this should be true for the filter functions, but it is imporant just to make sure
88 | for j=0, nbands-1 do begin
89 | case strlowcase(filter) of
90 | 'sobel' : edgeBand = sobel(tile_data[*,*,j])
91 | 'prewitt' : edgeBand = prewitt(tile_data[*,*,j])
92 | 'roberts' : edgeBand = roberts(tile_data[*,*,j])
93 | 'emboss' : edgeBand = emboss(tile_data[*,*,j])
94 | 'edge_dog' : edgeBand = edge_dog(tile_data[*,*,j])
95 | 'shift_diff' : edgeBand = shift_diff(tile_data[*,*,j])
96 | 'laplacian' : edgeBand = laplacian(tile_data[*,*,j])
97 | endcase
98 |
99 | if (j eq 0) then begin
100 | ;preallocate an array to hold our edge data
101 | tile_edge_data = make_array(sub_rect[2] - sub_rect[0] + 1, sub_rect[3] - sub_rect[1] + 1, nbands, TYPE = edgeBand.typecode, /NOZERO)
102 | endif
103 |
104 | tile_edge_data[0,0,j] = edgeBand
105 | endfor
106 |
107 | ;if first sub rect, get the typecode and initialize our raster
108 | if (i eq 0) then begin
109 | typecode = tile_edge_data.typecode
110 | output_raster = ENVIRaster($
111 | DATA_TYPE = typecode,$
112 | NBANDS = nbands,$
113 | NCOLUMNS = dims[0],$
114 | NROWS = dims[1],$
115 | SPATIALREF = input_raster.SPATIALREF,$
116 | METADATA = meta,$
117 | URI = output_raster_uri)
118 | endif
119 |
120 | ;check pixel state
121 | idxOff = where(ps, countOff)
122 | if (countOff gt 0) then begin
123 | tile_data[idxOff] = -1
124 | endif
125 |
126 | ;get the output index locations
127 | idx_tile = output_tile_sub_rects[i]
128 |
129 | ;set the data
130 | sub = tile_edge_data[idx_tile[0]:idx_tile[2],idx_tile[1]:idx_tile[3],0:nbands-1]
131 | output_raster.SetData, sub, SUB_RECT = output_raster_tile_locations[i]
132 | endforeach
133 |
134 | ;save our output raster
135 | output_raster.save
136 | output_raster.close
137 | end
138 |
139 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/CONTRIBUTING.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Copyright (c) 2018 Harris Geospatial Solutions, Inc.
107 |
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
108 |
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
109 |
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.TRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This folder contains the source code used for the different tasks in the UAV Toolkit. They are separated by the overall tasks that they belong to. The task files are also going to be located in these subfolders.
102 |
103 |
104 |
--------------------------------------------------------------------------------
/docs/index/indexSearch.js:
--------------------------------------------------------------------------------
1 | //h+
2 | // Copyright (c) 2018 Harris Geospatial Solutions, Inc.
3 | //
4 | // Licensed under MIT. See LICENSE.txt for additional details and information.
5 | //h-
6 |
7 | //search function
8 | $(document).ready(function () {
9 | //get search box
10 | var searchBox = $("#searchInput")
11 |
12 | // get div for search results
13 | var resultdiv = $("#searchResults");
14 |
15 | //set search input as selected then loading page
16 | searchBox.focus();
17 |
18 | // Set up search
19 | var index, store;
20 |
21 | // Create index
22 | index = lunr.Index.load(indexJSON.index);
23 |
24 | //validate the index
25 | //this is for the purpose of debugging and it will be output to
26 | //the console if there are any problems
27 | Object.keys(index.documentStore.store)
28 | .forEach(function (documentId) {
29 | var documentTokens = index.documentStore.store[documentId];
30 |
31 | documentTokens.forEach(function (token) {
32 | if (index.corpusTokens.indexOf(token) == -1) {
33 | console.log('not in corpus tokens', documentId, token);
34 | }
35 |
36 | if (!index.tokenStore.has(token)) {
37 | console.log('not in token store', documentId, token);
38 | }
39 | });
40 | });
41 |
42 | // Create store
43 | //store contains: title, body, and section
44 | store = indexJSON.store;
45 |
46 | //setup before functions
47 | var typingTimer; //timer identifier
48 | var doneTypingInterval = 250; //time in ms, 5 second for example
49 |
50 | //on keyup, start the countdown or check for enter
51 | searchBox.on('keyup', function (event) {
52 | //pressed the enter key, otherwise use a timer
53 | if(event.keyCode == 13){
54 | doneTyping (event)
55 | } else {
56 | clearTimeout(typingTimer);
57 | typingTimer = setTimeout(function() {doneTyping(event);}, doneTypingInterval);
58 | }
59 | });
60 |
61 | //on keydown, clear the countdown
62 | searchBox.on('keydown', function () {
63 | clearTimeout(typingTimer);
64 | });
65 |
66 | //user is "finished typing," do something
67 | function doneTyping (event) {
68 | // Get query without white spaces
69 | var query = event.target.value.trim();
70 |
71 | // clear previous results
72 | resultdiv.hide();
73 | resultdiv.empty();
74 |
75 | //chekc for valid query
76 | if (query != "" && query.length > 1){
77 | //set result dir value for search item
78 | resultdiv.append('
Matches for "' + query + '"
');
79 |
80 | // Search for it
81 | var result = index.search(query);
82 |
83 | // check if we found matches
84 | if (result.length === 0) {
85 | // nothing found
86 | resultdiv.append('
No results found
');
87 | } else {
88 | // display what we found
89 | for (var item in result) {
90 | var href = result[item].ref;
91 | var searchitem = '