aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Services/MapImageService/MapImageService.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Services/MapImageService/MapImageService.cs')
-rw-r--r--OpenSim/Services/MapImageService/MapImageService.cs298
1 files changed, 298 insertions, 0 deletions
diff --git a/OpenSim/Services/MapImageService/MapImageService.cs b/OpenSim/Services/MapImageService/MapImageService.cs
new file mode 100644
index 0000000..27722bb
--- /dev/null
+++ b/OpenSim/Services/MapImageService/MapImageService.cs
@@ -0,0 +1,298 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 * The design of this map service is based on SimianGrid's PHP-based
28 * map service. See this URL for the original PHP version:
29 * https://github.com/openmetaversefoundation/simiangrid/
30 */
31
32using System;
33using System.Collections.Generic;
34using System.Drawing;
35using System.Drawing.Imaging;
36using System.IO;
37using System.Net;
38using System.Reflection;
39
40using Nini.Config;
41using log4net;
42using OpenMetaverse;
43
44using OpenSim.Framework;
45using OpenSim.Framework.Console;
46using OpenSim.Services.Interfaces;
47
48
49namespace OpenSim.Services.MapImageService
50{
51 public class MapImageService : IMapImageService
52 {
53 private static readonly ILog m_log =
54 LogManager.GetLogger(
55 MethodBase.GetCurrentMethod().DeclaringType);
56
57 private const int ZOOM_LEVELS = 8;
58 private const int IMAGE_WIDTH = 256;
59 private const int HALF_WIDTH = 128;
60 private const int JPEG_QUALITY = 80;
61
62 private static string m_TilesStoragePath = "maptiles";
63
64 private static object m_Sync = new object();
65 private static bool m_Initialized = false;
66 private static string m_WaterTileFile = string.Empty;
67 private static Color m_Watercolor = Color.FromArgb(29, 71, 95);
68
69 public MapImageService(IConfigSource config)
70 {
71 if (!m_Initialized)
72 {
73 m_Initialized = true;
74 m_log.Debug("[MAP IMAGE SERVICE]: Starting MapImage service");
75
76 IConfig serviceConfig = config.Configs["MapImageService"];
77 if (serviceConfig != null)
78 {
79 m_TilesStoragePath = serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath);
80 if (!Directory.Exists(m_TilesStoragePath))
81 Directory.CreateDirectory(m_TilesStoragePath);
82
83
84 m_WaterTileFile = Path.Combine(m_TilesStoragePath, "water.jpg");
85 if (!File.Exists(m_WaterTileFile))
86 {
87 Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH);
88 FillImage(waterTile, m_Watercolor);
89 waterTile.Save(m_WaterTileFile);
90 }
91 }
92 }
93 }
94
95 #region IMapImageService
96
97 public bool AddMapTile(int x, int y, byte[] imageData, out string reason)
98 {
99 reason = string.Empty;
100 string fileName = GetFileName(1, x, y);
101
102 lock (m_Sync)
103 {
104 try
105 {
106 using (FileStream f = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write))
107 f.Write(imageData, 0, imageData.Length);
108 }
109 catch (Exception e)
110 {
111 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save image file {0}: {1}", fileName, e);
112 reason = e.Message;
113 return false;
114 }
115
116 // Also save in png format?
117
118 // Stitch seven more aggregate tiles together
119 for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++)
120 {
121 // Calculate the width (in full resolution tiles) and bottom-left
122 // corner of the current zoom level
123 int width = (int)Math.Pow(2, (double)(zoomLevel - 1));
124 int x1 = x - (x % width);
125 int y1 = y - (y % width);
126
127 if (!CreateTile(zoomLevel, x1, y1))
128 {
129 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0} at zoom level {1}", fileName, zoomLevel);
130 reason = string.Format("Map tile at zoom level {0} failed", zoomLevel);
131 return false;
132 }
133 }
134 }
135
136 return true;
137 }
138
139 public byte[] GetMapTile(string fileName, out string format)
140 {
141 format = ".jpg";
142 string fullName = Path.Combine(m_TilesStoragePath, fileName);
143 if (File.Exists(fullName))
144 {
145 format = Path.GetExtension(fileName).ToLower();
146 m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format);
147 return File.ReadAllBytes(fullName);
148 }
149 else if (File.Exists(m_WaterTileFile))
150 {
151 return File.ReadAllBytes(m_WaterTileFile);
152 }
153 else
154 {
155 m_log.DebugFormat("[MAP IMAGE SERVICE]: unable to get file {0}", fileName);
156 return new byte[0];
157 }
158 }
159
160 #endregion
161
162
163 private string GetFileName(uint zoomLevel, int x, int y)
164 {
165 string extension = "jpg";
166 return Path.Combine(m_TilesStoragePath, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension));
167 }
168
169 private Bitmap GetInputTileImage(string fileName)
170 {
171 try
172 {
173 if (File.Exists(fileName))
174 return new Bitmap(fileName);
175 }
176 catch (Exception e)
177 {
178 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
179 }
180
181 return null;
182 }
183
184 private Bitmap GetOutputTileImage(string fileName)
185 {
186 try
187 {
188 if (File.Exists(fileName))
189 return new Bitmap(fileName);
190
191 else
192 {
193 // Create a new output tile with a transparent background
194 Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH, PixelFormat.Format24bppRgb);
195 bm.MakeTransparent();
196 return bm;
197 }
198 }
199 catch (Exception e)
200 {
201 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
202 }
203
204 return null;
205 }
206
207 private bool CreateTile(uint zoomLevel, int x, int y)
208 {
209 m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel);
210 int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2);
211 int thisWidth = (int)Math.Pow(2, (double)zoomLevel - 1);
212
213 // Convert x and y to the bottom left tile for this zoom level
214 int xIn = x - (x % prevWidth);
215 int yIn = y - (y % prevWidth);
216
217 // Convert x and y to the bottom left tile for the next zoom level
218 int xOut = x - (x % thisWidth);
219 int yOut = y - (y % thisWidth);
220
221 // Try to open the four input tiles from the previous zoom level
222 Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn));
223 Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn));
224 Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth));
225 Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth));
226
227 // Open the output tile (current zoom level)
228 string outputFile = GetFileName(zoomLevel, xOut, yOut);
229 Bitmap output = GetOutputTileImage(outputFile);
230 if (output == null)
231 return false;
232 FillImage(output, m_Watercolor);
233
234 if (inputBL != null)
235 {
236 ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0);
237 inputBL.Dispose();
238 }
239 if (inputBR != null)
240 {
241 ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0);
242 inputBR.Dispose();
243 }
244 if (inputTL != null)
245 {
246 ImageCopyResampled(output, inputTL, 0, 0, 0, 0);
247 inputTL.Dispose();
248 }
249 if (inputTR != null)
250 {
251 ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0);
252 inputTR.Dispose();
253 }
254
255 // Write the modified output
256 try
257 {
258 using (Bitmap final = new Bitmap(output))
259 {
260 output.Dispose();
261 final.Save(outputFile, ImageFormat.Jpeg);
262 }
263 }
264 catch (Exception e)
265 {
266 m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e);
267 }
268
269 // Save also as png?
270
271 return true;
272 }
273
274 #region Image utilities
275
276 private void FillImage(Bitmap bm, Color c)
277 {
278 for (int x = 0; x < bm.Width; x++)
279 for (int y = 0; y < bm.Height; y++)
280 bm.SetPixel(x, y, c);
281 }
282
283 private void ImageCopyResampled(Bitmap output, Bitmap input, int destX, int destY, int srcX, int srcY)
284 {
285 int resamplingRateX = 2; // (input.Width - srcX) / (output.Width - destX);
286 int resamplingRateY = 2; // (input.Height - srcY) / (output.Height - destY);
287
288 for (int x = destX; x < destX + HALF_WIDTH; x++)
289 for (int y = destY; y < destY + HALF_WIDTH; y++)
290 {
291 Color p = input.GetPixel(srcX + (x - destX) * resamplingRateX, srcY + (y - destY) * resamplingRateY);
292 output.SetPixel(x, y, p);
293 }
294 }
295
296 #endregion
297 }
298}