Downloads Documentation Community Contribute Demo






Show Sidebar
Login | Register

Changeset 4880

Show
Ignore:
Timestamp:
07/09/08 15:36:49 (6 months ago)
Author:
TorLye
Message:

gmapsimageviewer: It is now possible to save annotations from the image viewer. Added jQuery for ajax support. Created a separate class for manipulating pixel coordinates. Minor edits to image tile servlet.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • openmrs-modules/gmapsimageviewer/metadata/config.xml

    r4823 r4880  
    8080        <servlet-class>@MODULE_PACKAGE@.web.servlet.ImageTileServlet</servlet-class> 
    8181    </servlet> 
     82    <servlet> 
     83        <servlet-name>imageAnnotationServlet</servlet-name> 
     84        <servlet-class>@MODULE_PACKAGE@.web.servlet.ImageAnnotationServlet</servlet-class> 
     85    </servlet> 
    8286         
    8387        <!-- /Servlets --> 
  • openmrs-modules/gmapsimageviewer/metadata/messages.properties

    r4823 r4880  
    1 @MODULE_ID@.title=Gmaps Image Viewer 
     1@MODULE_ID@.title=Google Maps Image Viewer 
    22@MODULE_ID@.listobs=Complex obs list 
    33@MODULE_ID@.uploadimage=Upload image file 
  • openmrs-modules/gmapsimageviewer/web/module/portlets/imageViewerWindow.jsp

    r4823 r4880  
    1 <%@ include file="/WEB-INF/template/include.jsp" %> 
    2 <openmrs:htmlInclude file="/scripts/dojoConfig.js"></openmrs:htmlInclude> 
    3 <openmrs:htmlInclude file="/scripts/dojo/dojo.js"></openmrs:htmlInclude> 
     1<%@ include file="/WEB-INF/template/include.jsp"%> 
     2<script type="text/javascript" src="${pageContext.request.contextPath}/moduleResources/gmapsimageviewer/jquery-1.2.6.js"> </script> 
    43 
    5 <!-- TODO: Work out a better way to set the size of the div element --> 
    6 <div id="imageviewer" style="min-height: 200px; height: 70%; width:95%; position:absolute"></div> 
    7  
    8 <script src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAK2jLMsuJE3dL7ZcZP8t4UhQKh6nTEbwfNWbt16AGms22P3cb0hTN4CsK0tJDoi0G47ZC3nsV5-h5iw" type="text/javascript"></script> 
     4<script 
     5        src="http://maps.google.com/maps?file=api&amp;v=2&amp;key=ABQIAAAAK2jLMsuJE3dL7ZcZP8t4UhQKh6nTEbwfNWbt16AGms22P3cb0hTN4CsK0tJDoi0G47ZC3nsV5-h5iw" 
     6        type="text/javascript"></script> 
    97 
    108<script type="text/javascript"> 
     
    3937                        } 
    4038                         
    41                          
    42                          
    4339                        GEvent.addListener(map, "click", function(overlay,point) { 
    44                                 if (event.shiftKey) { 
     40                                //TODO figure out this bit 
     41                                //if (event.shiftKey) { 
    4542                                        createMarker(point); 
    46                                
     43                                //
    4744                        }); 
    4845                         
     
    6562                        map.setCenter(new GLatLng(83, -90), 2, custommap); 
    6663                        //TODO: set center based on image size 
    67                          
    68                         //map.addOverlay(createMarker(new GLatLng(83, -90), "The amazing bionic cat")); 
    6964                } 
    7065                else { 
     
    7570         
    7671        function createMarker(point) { 
    77                                 var marker = new GMarker(point); 
    78                                 map.addOverlay(marker); 
    79                                 marker.openInfoWindowHtml("<form id='newmarker'><textarea id='annotationtext' name='annotationtext' rows='3' cols='20'></textarea><br /><input type='submit' value='Save' /><input type='button' value='Cancel' onclick='cancelMarker(marker)' /></form>"); 
    80                         } 
     72                var marker = new GMarker(point); 
     73                var point = map.getCurrentMapType().getProjection().fromLatLngToPixel(marker.getLatLng(), map.getZoom()); 
     74                map.addOverlay(marker); 
     75                marker.openInfoWindowHtml( 
     76                        "<form id='newmarkerform' method='post'>" + 
     77                                "<input type='hidden' name='obsid' value='${model.obsId}' />" + 
     78                                "<input type='hidden' name='xlocation' value='" + point.x + "' />" + 
     79                                "<input type='hidden' name='ylocation' value='" + point.y + "' />" + 
     80                                "<input type='hidden' name='zoom' value='" + map.getZoom() + "' />" + 
     81                                "<textarea id='annotationtext' name='annotationtext' rows='3' cols='20'></textarea><br />" + 
     82                                "<input type='button' name='save' value='Save' onclick='saveMarker()' />" + 
     83                                "<input type='button' name='cancel' value='Cancel' onclick='map.closeInfoWindow()' />" + 
     84                        "</form>"); 
     85                 
     86                var listener = GEvent.addListener(map, "infowindowclose", function() { 
     87                        GEvent.removeListener(listener); 
     88                        map.removeOverlay(marker); 
     89                }); 
     90        } 
     91 
     92        function saveMarker() { 
     93                jQuery.post( 
     94                        "${pageContext.request.contextPath}/moduleServlet/gmapsimageviewer/imageAnnotationServlet", 
     95                        $("#newmarkerform").serialize(), 
     96                        function(data){ 
     97                                if(data.search(/ok/i) == -1) { 
     98                                        alert("Error"); 
     99                                } else { 
     100                                        alert("Saved"); 
     101                                } 
     102                        }); 
     103                 
     104                map.closeInfoWindow(); 
     105        } 
    81106                         
    82                         function cancelMarker(marker) { 
    83                                 map.closeInfoWindow(); 
    84                                 map.removeOverlay(marker); 
    85                         } 
     107        $(document).ready(function(){ 
     108                ImageViewerLoad(); 
     109        }); 
     110         
     111        //GUnload() releases the client-side resources used by Google Maps, to prevent browser memory leaks 
     112        window.onunload = GUnload; 
     113</script> 
    86114 
    87         dojo.addOnLoad(ImageViewerLoad); 
    88         //dojo.addOnUnload(GUnload); 
    89         dojo.addOnUnload(function(){GUnload()}); 
    90         //TODO: Error: name: TypeError 
    91         //message: Statement on line 71: Type mismatch (usually non-object value supplied where object required) 
    92          
    93         //GUnload() releases the client-side resources used by Google Maps, to prevent browser memory leaks 
    94 </script> 
     115 
     116<!-- TODO: Work out a better way to set the size of the div element --> 
     117<div id="imageviewer" 
     118        style="min-height: 200px; height: 70%; width: 95%; position: absolute"></div> 
  • openmrs-modules/gmapsimageviewer/web/src/org/openmrs/module/gmapsimageviewer/web/servlet/ImageTileServlet.java

    r4823 r4880  
    1414import javax.imageio.ImageReadParam; 
    1515import javax.imageio.ImageReader; 
    16 import javax.imageio.ImageTypeSpecifier; 
    1716import javax.imageio.stream.ImageInputStream; 
    1817import javax.servlet.ServletException; 
     
    2726import org.openmrs.api.ObsService; 
    2827import org.openmrs.api.context.Context; 
     28import org.openmrs.module.gmapsimageviewer.Pixel; 
    2929import org.openmrs.obs.handler.ImageHandler; 
    3030import org.openmrs.util.OpenmrsConstants; 
    31 import org.openmrs.util.OpenmrsUtil; 
    3231import org.openmrs.web.WebConstants; 
    3332 
    34 //TODO Remove most of the log messages when no longer needed 
    35  
    3633/** 
    37  * This servlet takes four GET parameters: obsId, x, y and z. 
    38  * If the given obsId identifies an obs with an associated  
    39  * ComplexDataFile, the servlet will try to decode the image 
    40  * and return an image tile suitable for the Google Maps image 
    41  * viewer. x and y gives the coordinates for the requested image 
    42  * tile. z gives the zoom level where the coordinates are valid. 
     34 * This servlet takes four GET parameters: obsId, x, y and z. If the given obsId 
     35 * identifies an obs with an associated ComplexDataFile, the servlet will try to 
     36 * decode the image and return an image tile suitable for the Google Maps image 
     37 * viewer. x and y gives the coordinates for the requested image tile. z gives 
     38 * the zoom level where the coordinates are valid. 
    4339 */ 
    4440public class ImageTileServlet extends HttpServlet { 
     
    4743    private static Log log = LogFactory.getLog(ImageTileServlet.class); 
    4844 
    49     // The maximum width (in pixels) of the image when displayed at zoom level 0 
    50     private static final int LEVEL_0_MAX_WIDTH = 100; 
    51  
    52     // The maximum height (in pixels) of the image when displayed at zoom level 
    53     // 0 
    54     private static final int LEVEL_0_MAX_HEIGHT = 100; 
    55  
    56     // The size (in pixels) of the image tile, which is always a square. 
    57     private static final int IMAGE_TILE_SIZE = 256; 
    58  
    5945    // The color to use on empty image tiles 
    6046    private static final Color BACKGROUND_COLOR = new Color(229, 227, 223); 
    61      
     47 
     48    // The size (in pixels) of the image tile, which is always a square. 
     49    public static final int IMAGE_TILE_SIZE = 256; 
     50 
    6251    @Override 
    6352    protected void doGet(HttpServletRequest request, 
    6453            HttpServletResponse response) throws ServletException, IOException { 
    65         log.info("Imageviewer: starting tile generator"); 
    66         String obsId = request.getParameter("obsId"); 
     54        log.info("gmapsimageviewer: starting tile generator"); 
    6755        HttpSession session = request.getSession(); 
    6856 
    69         int x, y, zoom
     57        int x, y, zoom, obsId
    7058 
    7159        // Read the requested zoom level plus x-y coordinates from GET request 
     
    7462            y = Integer.parseInt(request.getParameter("y")); 
    7563            zoom = Integer.parseInt(request.getParameter("z")); 
     64            obsId = Integer.parseInt(request.getParameter("obsId")); 
    7665        } catch (NumberFormatException e) { 
    77             //Just return an emptu tile if the x-y-z coordinates are invalid. 
     66            // Just return an empty tile if the x-y-z coordinates are invalid. 
    7867            ImageIO.write(getEmptyTile(), "JPEG", response.getOutputStream()); 
    7968            response.getOutputStream().close(); 
    8069            response.setContentType("image/jpeg"); 
    81             log.error("Imageviewer: Returning blank tile"); 
     70            log.error("gmapsimageviewer: Returning blank tile"); 
    8271            return; 
    8372        } 
    8473 
    85         if (obsId == null || obsId.length() == 0) { 
    86             session.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, "error.null"); 
    87             log.error("Imageviewer: obsid was null"); 
    88             return; 
    89         } 
    9074        if (!Context.hasPrivilege(OpenmrsConstants.PRIV_VIEW_PATIENTS)) { 
    9175            session.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, 
    9276                    "Privilege required: " 
    9377                            + OpenmrsConstants.PRIV_VIEW_PATIENTS); 
    94             session.setAttribute( 
    95                     WebConstants.OPENMRS_LOGIN_REDIRECT_HTTPSESSION_ATTR, 
    96                     request.getRequestURI() + "?" + request.getQueryString()); 
    97             response.sendRedirect(request.getContextPath() + "/login.htm"); 
    98             log.error("Imageviewer: insufficient priveleges"); 
     78            log.error("gmapsimageviewer: insufficient priveleges"); 
    9979            return; 
    10080        } 
     
    10686        // Get the image file 
    10787        File imageFile = ImageHandler.getComplexDataFile(complexObs); 
    108         log.info("Imageviewer: trying to open file " 
     88        log.info("gmapsimageviewer: trying to open file " 
    10989                + imageFile.getCanonicalPath()); 
    11090 
    11191        if (!imageFile.exists()) { 
    112             // Try searching in the Application Data Directory. 
    113             String fileName = imageFile.getName(); 
    114             File dir = OpenmrsUtil 
    115                     .getDirectoryInApplicationDataDirectory(Context 
    116                             .getAdministrationService().getGlobalProperty( 
    117                                     "obs.complex_obs_dir")); 
    118             imageFile = null; 
    119             imageFile = new File(dir, fileName); 
    120             log.info("Imageviewer: trying to open file (second attempt) " 
    121                     + imageFile.getCanonicalPath()); 
    122         } 
    123         if (!imageFile.exists()) { 
    124             log.error("Imageviewer: The file: " + imageFile 
     92            log.error("gmapsimageviewer: The file: " + imageFile 
    12593                    + " does not exist."); 
    12694            throw new ServletException("The file: " + imageFile 
    12795                    + " does not exist."); 
    12896        } 
    129         log.info("Imageviewer: Creating input stream"); 
    13097 
    13198        ImageInputStream iis = ImageIO.createImageInputStream(imageFile); 
     
    134101        reader.setInput(iis); 
    135102 
    136         int originalHeight = reader.getHeight(0); 
    137         int originalWidth = reader.getWidth(0); 
    138          
     103        Pixel originalSize = new Pixel(reader.getWidth(0), reader.getHeight(0)); 
     104 
    139105        /* 
    140106         * If the colorspace of the original image is TYPE_GRAY, all image 
    141107         * manipulation will be done in format TYPE_BYTE_GRAY. All other 
    142108         * colorspaces will be manipulated as TYPE_INT_RGB. This will cause 
    143          * format conversion during import in cases where the original image 
    144          * is not a 24-bit RGB. Java's image manipulation loops are highly  
     109         * format conversion during import in cases where the original image is 
     110         * not a 24-bit RGB. Java's image manipulation loops are highly 
    145111         * optimised for TYPE_INT_RGB, so it's best to stick with this format. 
    146112         */ 
    147113        int colorFormat = BufferedImage.TYPE_INT_RGB; 
    148         Iterator<ImageTypeSpecifier> types = reader.getImageTypes(0); 
    149         while(types.hasNext()) 
    150             log.info("ImageTypeSpecifier: "+types.next().getColorModel().getColorSpace().getType()); 
    151         if(reader.getImageTypes(0).next().getColorModel().getColorSpace().getType() == ColorSpace.TYPE_GRAY) 
     114        if (reader.getImageTypes(0).next().getColorModel().getColorSpace() 
     115                .getType() == ColorSpace.TYPE_GRAY) 
    152116            colorFormat = BufferedImage.TYPE_BYTE_GRAY; 
    153          
    154         // The size (in pixels) of the full image at zoom level 0 
    155         int level0Width = originalWidth; 
    156         int level0Height = originalHeight; 
    157  
    158         int scaleExponent = 0; 
    159         while (level0Width > LEVEL_0_MAX_WIDTH 
    160                 || level0Height > LEVEL_0_MAX_HEIGHT) { 
    161             level0Width /= 2; 
    162             level0Height /= 2; 
    163             scaleExponent++; 
    164         } 
    165         double scaleDivisor = Math.pow(2, scaleExponent - zoom); 
    166  
    167         // The size (in pixels) of the full image at the requested zoom level 
    168         int currentZoomLevelWidth = (int) Math.round(originalWidth 
    169                 / scaleDivisor); 
    170         int currentZoomLevelHeight = (int) Math.round(originalHeight 
    171                 / scaleDivisor); 
    172  
    173         log.info("Imageviewer: Level 0 size is " + level0Width + "x" 
    174                 + level0Height); 
    175         log.info("Imageviewer: scale divisor is " + scaleDivisor); 
    176         log.info("Imageviewer: new size is " + currentZoomLevelWidth + "x" 
    177                 + currentZoomLevelHeight); 
     117 
     118        double scaleDivisor = Pixel.getScaleDivisor(originalSize, zoom); 
     119 
     120        // The size of the full image at the requested zoom level 
     121        Pixel currentZoomLevelSize = originalSize 
     122                .convertFromOriginalSizeToZoomlevel(scaleDivisor); 
    178123 
    179124        int originalZoomLevelTileSize = (int) (IMAGE_TILE_SIZE * scaleDivisor); 
    180125 
    181         // The position (in pixel coordinates) of the upper left corner of the 
     126        // The position in the original image of the upper left corner of the 
    182127        // requested image tile 
    183         int xpos = originalZoomLevelTileSize * x; 
    184         int ypos = originalZoomLevelTileSize * y; 
    185  
    186         log.info("Imageviewer: Starting image tile carving"); 
     128        Pixel tilePosition = new Pixel(originalZoomLevelTileSize * x, 
     129                originalZoomLevelTileSize * y); 
    187130 
    188131        // Bounds check 
    189         if (xpos >= originalWidth || ypos >= originalHeight) { 
     132        if (tilePosition.x >= originalSize.x 
     133                || tilePosition.y >= originalSize.y) { 
    190134            response.setContentType("image/jpeg"); 
    191135            ImageIO.write(getEmptyTile(), "JPEG", response.getOutputStream()); 
    192136            response.getOutputStream().close(); 
    193             log.error("Imageviewer: Returning blank tile"); 
     137            log.error("gmapsimageviewer: Returning blank tile"); 
    194138            return; 
    195139        } 
    196140 
    197         int subsample = scaleDivisor > 2 ? (int) scaleDivisor - 1 : 1; 
    198  
    199         log.info("Imageviewer: Reading " + originalZoomLevelTileSize + "x" 
     141        int subsampleLevel = Pixel.getSubsampleLevel(originalSize, zoom); 
     142 
     143        int tileReadSize = originalZoomLevelTileSize / subsampleLevel; 
     144 
     145        log.info("gmapsimageviewer: Reading " + originalZoomLevelTileSize + "x" 
    200146                + originalZoomLevelTileSize + " image, but subsampling by " 
    201                 + subsample + " giving a resulting size of " 
    202                 + originalZoomLevelTileSize / subsample + "x" 
    203                 + originalZoomLevelTileSize / subsample); 
     147                + subsampleLevel + " giving a resulting size of " 
     148                + tileReadSize + "x" + tileReadSize); 
    204149 
    205150        // Create image tile 
    206         BufferedImage bufferedImageTile = new BufferedImage(originalZoomLevelTileSize/subsample, originalZoomLevelTileSize/subsample, colorFormat); 
     151        BufferedImage bufferedImageTile = new BufferedImage(tileReadSize, 
     152                tileReadSize, colorFormat); 
    207153 
    208154        ImageReadParam param = reader.getDefaultReadParam(); 
    209         param.setSourceRegion(new Rectangle(xpos, ypos
     155        param.setSourceRegion(new Rectangle(tilePosition.x, tilePosition.y
    210156                originalZoomLevelTileSize, originalZoomLevelTileSize)); 
    211         param.setSourceSubsampling(subsample, subsample, 0, 0); 
    212 //        param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(colorFormat)); 
     157        param.setSourceSubsampling(subsampleLevel, subsampleLevel, 0, 0); 
    213158        param.setDestination(bufferedImageTile); 
    214 //        bufferedImageTile = reader.read(0, param); 
    215159        reader.read(0, param); 
    216160        reader.dispose(); 
    217          
    218  
    219         switch (bufferedImageTile.getType()) { 
    220         case BufferedImage.TYPE_BYTE_GRAY: 
    221             log.info("Imageviewer: TYPE_BYTE_GRAY"); 
    222             break; 
    223         case BufferedImage.TYPE_INT_RGB: 
    224             log.info("Imageviewer: TYPE_INT_RGB"); 
    225             break; 
    226         default: 
    227             //Should not happen! 
    228             log.info("Imageviewer: Unknown type " 
    229                             + bufferedImageTile.getType()); 
    230             break; 
    231         } 
    232          
     161 
    233162        /* 
    234          * If the portion of the image that was read does not have the 
    235          * expected size, it is because the tile covers an area partially 
    236          * outside the image. This only happens at the right and bottom 
    237          * edges. If this is the case, the image should be padded with 
    238          * BACKGROUND_COLOR to make it the correct size. 
     163         * If the portion of the image that was read does not have the expected 
     164         * size, it is because the tile covers an area partially outside the 
     165         * image. This only happens at the right and bottom edges. If this is 
     166         * the case, the image should be padded with BACKGROUND_COLOR to make it 
     167         * the correct size. 
    239168         */ 
    240         if (bufferedImageTile.getWidth() < originalZoomLevelTileSize 
    241                 / subsample 
    242                 || bufferedImageTile.getHeight() < originalZoomLevelTileSize 
    243                         / subsample) { 
    244             BufferedImage newImageTile = new BufferedImage( 
    245                     originalZoomLevelTileSize / subsample, 
    246                     originalZoomLevelTileSize / subsample, bufferedImageTile 
    247                             .getType()); 
     169        if (bufferedImageTile.getWidth() < tileReadSize 
     170                || bufferedImageTile.getHeight() < tileReadSize) { 
     171            BufferedImage newImageTile = new BufferedImage(tileReadSize, 
     172                    tileReadSize, bufferedImageTile.getType()); 
    248173            Graphics2D graphics = newImageTile.createGraphics(); 
    249174            graphics.setColor(BACKGROUND_COLOR); 
     
    255180        } 
    256181 
    257         log.info("Imageviewer: Completed image tile carving"); 
    258         log.info("Imageviewer: Starting image tile resize"); 
    259          
    260182        // Scale image 
    261183        BufferedImage scaledBufferedImage = new BufferedImage(IMAGE_TILE_SIZE, 
     
    267189                IMAGE_TILE_SIZE, null); 
    268190        graphics.dispose(); 
    269         log.info("Imageviewer: Completed image tile resize"); 
    270191 
    271192        // Write the result to response 
     
    274195        response.setContentType("image/jpeg"); 
    275196        response.getOutputStream().close(); 
    276         log.info("Imageviewer: tile completed!"); 
     197        log.info("gmapsimageviewer: tile completed!"); 
    277198    } 
    278199 
     
    291212        return emptyTile; 
    292213    } 
     214 
    293215}