Changeset 4880
- Timestamp:
- 07/09/08 15:36:49 (6 months ago)
- Files:
-
- openmrs-modules/gmapsimageviewer/metadata/config.xml (modified) (1 diff)
- openmrs-modules/gmapsimageviewer/metadata/messages.properties (modified) (1 diff)
- openmrs-modules/gmapsimageviewer/src/org/openmrs/module/gmapsimageviewer/Pixel.java (added)
- openmrs-modules/gmapsimageviewer/web/module/portlets/imageViewerWindow.jsp (modified) (4 diffs)
- openmrs-modules/gmapsimageviewer/web/module/resources (added)
- openmrs-modules/gmapsimageviewer/web/module/resources/jquery-1.2.6.js (added)
- openmrs-modules/gmapsimageviewer/web/src/org/openmrs/module/gmapsimageviewer/web/servlet/ImageAnnotationServlet.java (added)
- openmrs-modules/gmapsimageviewer/web/src/org/openmrs/module/gmapsimageviewer/web/servlet/ImageTileServlet.java (modified) (10 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
openmrs-modules/gmapsimageviewer/metadata/config.xml
r4823 r4880 80 80 <servlet-class>@MODULE_PACKAGE@.web.servlet.ImageTileServlet</servlet-class> 81 81 </servlet> 82 <servlet> 83 <servlet-name>imageAnnotationServlet</servlet-name> 84 <servlet-class>@MODULE_PACKAGE@.web.servlet.ImageAnnotationServlet</servlet-class> 85 </servlet> 82 86 83 87 <!-- /Servlets --> openmrs-modules/gmapsimageviewer/metadata/messages.properties
r4823 r4880 1 @MODULE_ID@.title=G maps Image Viewer1 @MODULE_ID@.title=Google Maps Image Viewer 2 2 @MODULE_ID@.listobs=Complex obs list 3 3 @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> 4 3 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&v=2&key=ABQIAAAAK2jLMsuJE3dL7ZcZP8t4UhQKh6nTEbwfNWbt16AGms22P3cb0hTN4CsK0tJDoi0G47ZC3nsV5-h5iw" type="text/javascript"></script> 4 <script 5 src="http://maps.google.com/maps?file=api&v=2&key=ABQIAAAAK2jLMsuJE3dL7ZcZP8t4UhQKh6nTEbwfNWbt16AGms22P3cb0hTN4CsK0tJDoi0G47ZC3nsV5-h5iw" 6 type="text/javascript"></script> 9 7 10 8 <script type="text/javascript"> … … 39 37 } 40 38 41 42 43 39 GEvent.addListener(map, "click", function(overlay,point) { 44 if (event.shiftKey) { 40 //TODO figure out this bit 41 //if (event.shiftKey) { 45 42 createMarker(point); 46 }43 //} 47 44 }); 48 45 … … 65 62 map.setCenter(new GLatLng(83, -90), 2, custommap); 66 63 //TODO: set center based on image size 67 68 //map.addOverlay(createMarker(new GLatLng(83, -90), "The amazing bionic cat"));69 64 } 70 65 else { … … 75 70 76 71 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 } 81 106 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> 86 114 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 14 14 import javax.imageio.ImageReadParam; 15 15 import javax.imageio.ImageReader; 16 import javax.imageio.ImageTypeSpecifier;17 16 import javax.imageio.stream.ImageInputStream; 18 17 import javax.servlet.ServletException; … … 27 26 import org.openmrs.api.ObsService; 28 27 import org.openmrs.api.context.Context; 28 import org.openmrs.module.gmapsimageviewer.Pixel; 29 29 import org.openmrs.obs.handler.ImageHandler; 30 30 import org.openmrs.util.OpenmrsConstants; 31 import org.openmrs.util.OpenmrsUtil;32 31 import org.openmrs.web.WebConstants; 33 32 34 //TODO Remove most of the log messages when no longer needed35 36 33 /** 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. 43 39 */ 44 40 public class ImageTileServlet extends HttpServlet { … … 47 43 private static Log log = LogFactory.getLog(ImageTileServlet.class); 48 44 49 // The maximum width (in pixels) of the image when displayed at zoom level 050 private static final int LEVEL_0_MAX_WIDTH = 100;51 52 // The maximum height (in pixels) of the image when displayed at zoom level53 // 054 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 59 45 // The color to use on empty image tiles 60 46 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 62 51 @Override 63 52 protected void doGet(HttpServletRequest request, 64 53 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"); 67 55 HttpSession session = request.getSession(); 68 56 69 int x, y, zoom ;57 int x, y, zoom, obsId; 70 58 71 59 // Read the requested zoom level plus x-y coordinates from GET request … … 74 62 y = Integer.parseInt(request.getParameter("y")); 75 63 zoom = Integer.parseInt(request.getParameter("z")); 64 obsId = Integer.parseInt(request.getParameter("obsId")); 76 65 } catch (NumberFormatException e) { 77 // Just return an emptutile if the x-y-z coordinates are invalid.66 // Just return an empty tile if the x-y-z coordinates are invalid. 78 67 ImageIO.write(getEmptyTile(), "JPEG", response.getOutputStream()); 79 68 response.getOutputStream().close(); 80 69 response.setContentType("image/jpeg"); 81 log.error(" Imageviewer: Returning blank tile");70 log.error("gmapsimageviewer: Returning blank tile"); 82 71 return; 83 72 } 84 73 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 }90 74 if (!Context.hasPrivilege(OpenmrsConstants.PRIV_VIEW_PATIENTS)) { 91 75 session.setAttribute(WebConstants.OPENMRS_ERROR_ATTR, 92 76 "Privilege required: " 93 77 + 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"); 99 79 return; 100 80 } … … 106 86 // Get the image file 107 87 File imageFile = ImageHandler.getComplexDataFile(complexObs); 108 log.info(" Imageviewer: trying to open file "88 log.info("gmapsimageviewer: trying to open file " 109 89 + imageFile.getCanonicalPath()); 110 90 111 91 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 125 93 + " does not exist."); 126 94 throw new ServletException("The file: " + imageFile 127 95 + " does not exist."); 128 96 } 129 log.info("Imageviewer: Creating input stream");130 97 131 98 ImageInputStream iis = ImageIO.createImageInputStream(imageFile); … … 134 101 reader.setInput(iis); 135 102 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 139 105 /* 140 106 * If the colorspace of the original image is TYPE_GRAY, all image 141 107 * manipulation will be done in format TYPE_BYTE_GRAY. All other 142 108 * 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 highly109 * format conversion during import in cases where the original image is 110 * not a 24-bit RGB. Java's image manipulation loops are highly 145 111 * optimised for TYPE_INT_RGB, so it's best to stick with this format. 146 112 */ 147 113 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) 152 116 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); 178 123 179 124 int originalZoomLevelTileSize = (int) (IMAGE_TILE_SIZE * scaleDivisor); 180 125 181 // The position (in pixel coordinates)of the upper left corner of the126 // The position in the original image of the upper left corner of the 182 127 // 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); 187 130 188 131 // Bounds check 189 if (xpos >= originalWidth || ypos >= originalHeight) { 132 if (tilePosition.x >= originalSize.x 133 || tilePosition.y >= originalSize.y) { 190 134 response.setContentType("image/jpeg"); 191 135 ImageIO.write(getEmptyTile(), "JPEG", response.getOutputStream()); 192 136 response.getOutputStream().close(); 193 log.error(" Imageviewer: Returning blank tile");137 log.error("gmapsimageviewer: Returning blank tile"); 194 138 return; 195 139 } 196 140 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" 200 146 + 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); 204 149 205 150 // Create image tile 206 BufferedImage bufferedImageTile = new BufferedImage(originalZoomLevelTileSize/subsample, originalZoomLevelTileSize/subsample, colorFormat); 151 BufferedImage bufferedImageTile = new BufferedImage(tileReadSize, 152 tileReadSize, colorFormat); 207 153 208 154 ImageReadParam param = reader.getDefaultReadParam(); 209 param.setSourceRegion(new Rectangle( xpos, ypos,155 param.setSourceRegion(new Rectangle(tilePosition.x, tilePosition.y, 210 156 originalZoomLevelTileSize, originalZoomLevelTileSize)); 211 param.setSourceSubsampling(subsample, subsample, 0, 0); 212 // param.setDestinationType(ImageTypeSpecifier.createFromBufferedImageType(colorFormat)); 157 param.setSourceSubsampling(subsampleLevel, subsampleLevel, 0, 0); 213 158 param.setDestination(bufferedImageTile); 214 // bufferedImageTile = reader.read(0, param);215 159 reader.read(0, param); 216 160 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 233 162 /* 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 partially236 * outside the image. This only happens at the right and bottom237 * edges. If this is the case, the image should be padded with238 * BACKGROUND_COLOR to make itthe 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. 239 168 */ 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()); 248 173 Graphics2D graphics = newImageTile.createGraphics(); 249 174 graphics.setColor(BACKGROUND_COLOR); … … 255 180 } 256 181 257 log.info("Imageviewer: Completed image tile carving");258 log.info("Imageviewer: Starting image tile resize");259 260 182 // Scale image 261 183 BufferedImage scaledBufferedImage = new BufferedImage(IMAGE_TILE_SIZE, … … 267 189 IMAGE_TILE_SIZE, null); 268 190 graphics.dispose(); 269 log.info("Imageviewer: Completed image tile resize");270 191 271 192 // Write the result to response … … 274 195 response.setContentType("image/jpeg"); 275 196 response.getOutputStream().close(); 276 log.info(" Imageviewer: tile completed!");197 log.info("gmapsimageviewer: tile completed!"); 277 198 } 278 199 … … 291 212 return emptyTile; 292 213 } 214 293 215 }