; docformat = 'rst' ; ; NAME: ; cgShapeInfo ; ; PURPOSE: ; The purpose of this program is allow the user to browse a very narrow ; selection of shapefiles. Namely, those containing geographical shapes ; in latitude and longitude coordinates. In other words, shapefiles ; containing maps. File attributes are listed in the left-hand list ; widget. Clicking on a file attribute, will list all the entity ; attributes for that file attribute in the right-hand list. Clicking ; an entity attribute with list the type of entity, and the X and Y ; bounds of that entity (shown as LON and LAT, respectively), in the ; statusbar at the bottom of the display. ; ; Knowing the attribute and entity names of a shapefile will give ; you insight into how to read and display the information in the file. ; For examples, see the Coyote Library program cgDrawShapes. ; ;******************************************************************************************; ; ; ; Copyright (c) 2012, by Fanning Software Consulting, Inc. All rights reserved. ; ; ; ; Redistribution and use in source and binary forms, with or without ; ; modification, are permitted provided that the following conditions are met: ; ; ; ; * Redistributions of source code must retain the above copyright ; ; notice, this list of conditions and the following disclaimer. ; ; * Redistributions in binary form must reproduce the above copyright ; ; notice, this list of conditions and the following disclaimer in the ; ; documentation and/or other materials provided with the distribution. ; ; * Neither the name of Fanning Software Consulting, Inc. nor the names of its ; ; contributors may be used to endorse or promote products derived from this ; ; software without specific prior written permission. ; ; ; ; THIS SOFTWARE IS PROVIDED BY FANNING SOFTWARE CONSULTING, INC. ''AS IS'' AND ANY ; ; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ; ; OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT ; ; SHALL FANNING SOFTWARE CONSULTING, INC. BE LIABLE FOR ANY DIRECT, INDIRECT, ; ; INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED ; ; TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ; ; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ; ; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ; ; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ; ; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ; ;******************************************************************************************; ; ;+-------------------------------------------------------------------------- ; The purpose of this program is allow the user to browse a very narrow ; selection of shapefiles. Namely, those containing geographical shapes ; in latitude and longitude coordinates. In other words, shapefiles ; containing maps. File attributes are listed in the left-hand list ; widget. Clicking on a file attribute, will list all the entity ; attributes for that file attribute in the right-hand list. Clicking ; an entity attribute with list the type of entity, and the X and Y ; bounds of that entity (shown as LON and LAT, respectively), in the ; statusbar at the bottom of the display. ; ; Knowing the attribute and entity names of a shapefile will give ; you insight into how to read and display the information in the file. ; For examples, see the Coyote Library program cgDrawShapes. ; ; :Categories: ; Utilities ; ; :Author: ; FANNING SOFTWARE CONSULTING:: ; David W. Fanning ; 1645 Sheely Drive ; Fort Collins, CO 80526 USA ; Phone: 970-221-0438 ; E-mail: david@idlcoyote.com ; Coyote's Guide to IDL Programming: http://www.idlcoyote.com ; ; :Examples: ; To learn about and draw the states.shp file in the IDL examples directory:: ; filename = Filepath(SubDir=['examples','data'], 'states.shp') ; cgShapeInfo, filename, XRANGE=xr, YRANGE=yr ; cgPlot, xr, yr, /NoData, XStyle=1, YStyle=1 ; cgDrawShapes, filename ; ; :History: ; Modification History:: ; Written by David W. Fanning, 21 October 2006. ; Slight modifications to the interface. 14 May 2010. DWF. ; Added XRANGE and YRANGE output keywords. 15 May 2010. DWF. ; Renamed cgShapeFile from Shapefile. 20 Aug 2012. DWF. ; Modified so that when the user selects a new Attribute Name, the Attribue Value index stays ; the same. 24 Nov 2013. DWF. ; Fixed a type with a progress bar. 26 March 2014. DWF. ; ; :Copyright: ; Copyright (c) 2006-2013, Fanning Software Consulting, Inc. ;- ; ;+ ; The event handler for the program. ; ; :Params: ; event: in, required, type=struct ; The event structure passed by the window manager. ;- PRO cgShapeInfo_Events, event Compile_Opt idl2 ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel void = cgErrorMsg() RETURN ENDIF ; Get the info structure. Widget_Control, event.top, Get_UValue=info ; Possible events identified by UVALUE. Widget_Control, event.id, Get_UValue=theEvent CASE theEvent OF ; User selected entity attribute. Get the particulars for this entity. 'ENTITYLIST': BEGIN types = IntArr(N_Elements(*(*info).entities)) bounds = FltArr(N_Elements(*(*info).entities)) thisEntity = (*(*info).entities)[event.index] (*info).valueIndex = event.index types = thisEntity.shape_type bounds = thisEntity.bounds typeIndex = Where((*info).typecode EQ types, count) IF count EQ 0 THEN BEGIN ok = Dialog_Message('Unknown type code: ' + types + ' Returning.') RETURN ENDIF space = String(Replicate(32B, 5)) type = 'Type = ' + ((*info).shapetype[typeIndex])[0] lat = 'Y Bounds = [' + StrTrim(bounds[1],2) + ', ' + StrTrim(bounds[5],2) + ']' lon = 'X Bounds = [' + StrTrim(bounds[0],2) + ', ' + StrTrim(bounds[4],2) + ']' idx = 'Index = ' + StrTrim(event.index,2) Widget_Control, (*info).statusbar, $ Set_Value=type[0] + space + lon + space + lat + space + idx END ; User selected a file attribute. Get the entity attributes associated with this file attribute. 'NAMELIST': BEGIN num = N_Elements(*(*info).entities) entityAttributes = StrArr(num) IF num GT 1000 THEN BEGIN progressBar = Obj_New("CGPROGRESSBAR", Title='Collecting Attribute Values...') progressBar -> Start ENDIF usingProgress = Obj_Valid(progressBar) FOR j=0,num-1 DO BEGIN thisEntity = (*(*info).entities)[j] entityAttributes[j] = StrUpCase(StrTrim((*thisEntity.attributes).(event.index), 2)) IF usingProgress THEN progressBar -> Update, (Float(j)/num)*100 ENDFOR IF usingProgress THEN ProgressBar -> Destroy (*info).nameIndex = event.index Widget_Control, (*info).entityList, Set_Value=entityAttributes *(*info).entityAttributes = entityAttributes Widget_Control, (*info).statusbar, Set_Value=String(Replicate(32B, 150)) Widget_Control, (*info).entityList, Set_List_Select=(*info).valueIndex END ; User wants to open new shapefile. 'OPENFILE': BEGIN CD, CURRENT=thisDir, (*info).directory filename = Dialog_Pickfile(Title='Select Shapefile for Input...', Filter='*.shp') CD, thisDir IF filename EQ "" THEN RETURN (*info).directory = File_DirName(filename) ; Clean up current shapefile information. Heap_Free, (*info).entities Ptr_Free, (*info).entityAttributes Obj_Destroy, (*info).shapefile ; Create a new shapefile object shapefile = Obj_New('IDLffShape', filename) IF Obj_Valid(shapefile) EQ 0 THEN BEGIN Message, 'Specified file (' + File_Basename(filename) + $ ') does not appear to be a shapefile. Returning...' ENDIF ; Get the attribute names from the shape file. shapefile -> GetProperty, ATTRIBUTE_NAMES=theNames theNames = StrUpCase(StrTrim(theNames, 2)) ; Get all the attribute pointers from the file. These are the entities. entities = Ptr_New(/Allocate_Heap) *entities = shapefile -> GetEntity(/All, /Attributes) ; Load the info structure. (*info).shapefile = shapefile (*info).entities = entities entityAttributes = StrArr(N_Elements(*(*info).entities)) FOR j=0,N_Elements(*(*info).entities)-1 DO BEGIN thisEntity = (*(*info).entities)[j] entityAttributes[j] = StrUpCase(StrTrim((*thisEntity.attributes).(0), 2)) ENDFOR Widget_Control, (*info).namelist, Set_List_Select=0 (*info).entityAttributes = Ptr_New(entityAttributes) Widget_Control, (*info).nameList, Set_Value=theNames Widget_Control, (*info).entityList, Set_Value=entityAttributes Widget_Control, (*info).statusbar, Set_Value=String(Replicate(32B, 150)) END 'QUIT': Widget_Control, event.top, /Destroy ELSE: Print, 'Event not currently handled. ENDCASE END ; ----------------------------------------------------------------------------------------- ;+ ; The clean-up routine for the program. ; ; :Params: ; tlb: in, required, type=long ; The identifier of the widget that just died. ;- PRO cgShapeInfo_Cleanup, tlb Widget_Control, tlb, Get_UValue=info Heap_Free, (*info).entities Ptr_Free, (*info).entityAttributes Obj_Destroy, (*info).shapefile Ptr_Free, info END ; ----------------------------------------------------------------------------------------- ;+ ; The calling program for learning more about the contents of the shapefile. ; ; :Params: ; filename: in, optional, type=string ; The name of the shapefile you wish to browse. If not provided, the user ; will be asked to select a shapefile. ; ; :Keywords: ; xrange: out, optional, type=float ; The X range of the shapefile contents in the native units of the shapefile. ; yrange: out, optional, type=float ; The Y range of the shapefile contents in the native units of the shapefile. ;- PRO cgShapeInfo, filename, XRANGE=xrange, YRANGE=yrange Compile_Opt idl2 ; Error handling. Catch, theError IF theError NE 0 THEN BEGIN Catch, /Cancel void = cgErrorMsg() RETURN ENDIF ; Ask the user to select file if one is not provided. IF N_Elements(filename) EQ 0 THEN BEGIN filename = Dialog_Pickfile(Title='Select Shapefile for Input...', Filter='*.shp') IF filename EQ "" THEN RETURN ENDIF ; Create a shapefile object shapefile = Obj_New('IDLffShape', filename) directory = File_DirName(filename) IF Obj_Valid(shapefile) EQ 0 THEN BEGIN Message, 'Specified file (' + File_Basename(filename) + $ ') does not appear to be a shapefile. Returning...' ENDIF ; Get the attribute names from the shape file. shapefile -> GetProperty, ATTRIBUTE_NAMES=theNames theNames = StrUpCase(StrTrim(theNames, 2)) ; Get all the attribute pointers from the file. These are the entities. entities = Ptr_New(/Allocate_Heap) *entities = shapefile -> GetEntity(/All, /Attributes) ; Create the widgets. tlb = Widget_Base(Title='ShapeFile Information', Column=1, MBAR=menuID) listBase = Widget_Base(tlb, Row=1, XPAD=0, YPAD=0, SPACE=0) ; Menubar fileID = Widget_Button(menuID, Value='File') button = Widget_Button(fileID, Value='Open New Shapefile', UVALUE='OPENFILE') button = Widget_Button(fileID, Value='Quit', UVALUE='QUIT') ; Shapefile Lists. nameBase = Widget_Base(listBase, Column=1) label = Widget_Label(nameBase, Value='Attribute Name') nameList = Widget_List(nameBase, Value=theNames, YSize=25, XSIZE=40, UVALUE='NAMELIST') Widget_Control, nameList, Set_List_Select=0 attIndex = 0 ; Cycle through each entity and get the attribute entityAttributes = StrArr(N_Elements(*entities)) entityTypes = IntArr(N_Elements(*entities)) entityMinX = FltArr(N_Elements(*entities)) entityMaxX = FltArr(N_Elements(*entities)) entityMiny = FltArr(N_Elements(*entities)) entityMaxY = FltArr(N_Elements(*entities)) FOR j=0,N_Elements(*entities)-1 DO BEGIN thisEntity = (*entities)[j] entityAttributes[j] = StrUpCase(StrTrim((*thisEntity.attributes).(attIndex), 2)) entityTypes[j] = thisEntity.shape_type entityMinX[j] = thisEntity.bounds[0] entityMaxX[j] = thisEntity.bounds[4] entityMiny[j] = thisEntity.bounds[1] entityMaxY[j] = thisEntity.bounds[5] ENDFOR xrange = [Min(entityMinX), Max(entityMaxX)] yrange = [Min(entityMinY), Max(entityMaxY)] ; Print, 'XRange = [' + Strtrim(Min(entityMinX),2) + ',' + StrTrim(Max(entityMaxX),2) + ']' ; Print, 'YRange = [' + Strtrim(Min(entityMinY),2) + ',' + StrTrim(Max(entityMaxY),2) + ']' ; FOR j=0,N_Elements(*entities)-1 DO Print, entityMinx[j], entityMaxx[j], entityminy[j], entitymaxy[j] ; Entity attribute list. nameBase = Widget_Base(listBase, Column=1) label = Widget_Label(nameBase, Value='Attribute Value') entityList = Widget_List(nameBase, Value=entityAttributes, YSize=25, XSIZE=75, UVALUE='ENTITYLIST') ; Entity statusbar widgets. infobase = Widget_Base(tlb, Row=1, XPAD=0, YPAD=0, SPACE=0) label = Widget_Label(infobase, Value='Entity Info: ') ; Size the statusbar appropriately. gb = Widget_Info(infobase, /Geometry) gl = Widget_Info(label, /Geometry) statusbar = Widget_Label(infobase,/Sunken_Frame, SCR_XSIZE=gb.scr_xsize-gl.scr_xsize, $ Value=String(Replicate(32B, 150))) cgCenterTLB, tlb Widget_Control, tlb, /Realize ; Information to identify entity types. typecode = [ 1, 3, 5, 8, 11, 13, 15, 18, 21, 23, 25, 28, 31] shapetype = ['Point', 'Polyline', 'Polygon', 'MultiPoint', 'PointZ', 'PointLineZ', 'PolygonZ', $ 'MultiPointZ', 'PointM', 'PolyLineM', 'PolygonM', 'MultiPointM', 'MultiPatch'] ; Create info structure and store it. info = Ptr_New({entities:entities, namelist:namelist, entitylist:entitylist, $ entityAttributes:Ptr_New(entityAttributes, /No_Copy), shapefile:shapefile, $ typecode:typecode, shapetype:shapetype, statusbar:statusbar, directory:directory, $ nameIndex:0, valueIndex:0}, /No_Copy) Widget_Control, tlb, Set_UValue=info ; Start it up. XManager, 'cgshapeinfo', tlb, Event_Handler='cgShapeInfo_Events', $ Cleanup='cgShapeInfo_Cleanup', /No_Block END ; -----------------------------------------------------------------------------------------