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.cs295
1 files changed, 295 insertions, 0 deletions
diff --git a/OpenSim/Services/MapImageService/MapImageService.cs b/OpenSim/Services/MapImageService/MapImageService.cs
new file mode 100644
index 0000000..596c4a6
--- /dev/null
+++ b/OpenSim/Services/MapImageService/MapImageService.cs
@@ -0,0 +1,295 @@
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
28using System;
29using System.Collections.Generic;
30using System.Drawing;
31using System.Drawing.Imaging;
32using System.IO;
33using System.Net;
34using System.Reflection;
35
36using Nini.Config;
37using log4net;
38using OpenMetaverse;
39
40using OpenSim.Framework;
41using OpenSim.Framework.Console;
42using OpenSim.Services.Interfaces;
43
44
45namespace OpenSim.Services.MapImageService
46{
47 public class MapImageService : IMapImageService
48 {
49 private static readonly ILog m_log =
50 LogManager.GetLogger(
51 MethodBase.GetCurrentMethod().DeclaringType);
52
53 private const int ZOOM_LEVELS = 8;
54 private const int IMAGE_WIDTH = 256;
55 private const int HALF_WIDTH = 128;
56 private const int JPEG_QUALITY = 80;
57
58 private static string m_TilesStoragePath = "maptiles";
59
60 private static object m_Sync = new object();
61 private static bool m_Initialized = false;
62 private static string m_WaterTileFile = string.Empty;
63 private static Color m_Watercolor = Color.FromArgb(29, 71, 95);
64
65 public MapImageService(IConfigSource config)
66 {
67 if (!m_Initialized)
68 {
69 m_Initialized = true;
70 m_log.Debug("[MAP IMAGE SERVICE]: Starting MapImage service");
71
72 IConfig serviceConfig = config.Configs["MapImageService"];
73 if (serviceConfig != null)
74 {
75 m_TilesStoragePath = serviceConfig.GetString("TilesStoragePath", m_TilesStoragePath);
76 if (!Directory.Exists(m_TilesStoragePath))
77 Directory.CreateDirectory(m_TilesStoragePath);
78
79
80 m_WaterTileFile = Path.Combine(m_TilesStoragePath, "water.jpg");
81 if (!File.Exists(m_WaterTileFile))
82 {
83 Bitmap waterTile = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH);
84 FillImage(waterTile, m_Watercolor);
85 waterTile.Save(m_WaterTileFile);
86 }
87 }
88 }
89 }
90
91 #region IMapImageService
92
93 public bool AddMapTile(int x, int y, byte[] imageData, out string reason)
94 {
95 reason = string.Empty;
96 string fileName = GetFileName(1, x, y);
97
98 lock (m_Sync)
99 {
100 try
101 {
102 using (FileStream f = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.Write))
103 f.Write(imageData, 0, imageData.Length);
104 }
105 catch (Exception e)
106 {
107 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to save image file {0}: {1}", fileName, e);
108 reason = e.Message;
109 return false;
110 }
111
112 // Also save in png format?
113
114 // Stitch seven more aggregate tiles together
115 for (uint zoomLevel = 2; zoomLevel <= ZOOM_LEVELS; zoomLevel++)
116 {
117 // Calculate the width (in full resolution tiles) and bottom-left
118 // corner of the current zoom level
119 int width = (int)Math.Pow(2, (double)(zoomLevel - 1));
120 int x1 = x - (x % width);
121 int y1 = y - (y % width);
122
123 if (!CreateTile(zoomLevel, x1, y1))
124 {
125 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to create tile for {0} at zoom level {1}", fileName, zoomLevel);
126 reason = string.Format("Map tile at zoom level {0} failed", zoomLevel);
127 return false;
128 }
129 }
130 }
131
132 return true;
133 }
134
135 public byte[] GetMapTile(string fileName, out string format)
136 {
137 format = "jpg";
138 string fullName = Path.Combine(m_TilesStoragePath, fileName);
139 if (File.Exists(fullName))
140 {
141 format = Path.GetExtension(fileName).ToLower();
142 m_log.DebugFormat("[MAP IMAGE SERVICE]: Found file {0}, extension {1}", fileName, format);
143 return File.ReadAllBytes(fullName);
144 }
145 else if (File.Exists(m_WaterTileFile))
146 {
147 m_log.DebugFormat("[MAP IMAGE SERVICE]: File not found {0}, sending water", fileName);
148 return File.ReadAllBytes(m_WaterTileFile);
149 }
150 else
151 {
152 m_log.DebugFormat("[MAP IMAGE SERVICE]: unable to get file {0}", fileName);
153 return new byte[0];
154 }
155 }
156
157 #endregion
158
159
160 private string GetFileName(uint zoomLevel, int x, int y)
161 {
162 string extension = "jpg";
163 return Path.Combine(m_TilesStoragePath, string.Format("map-{0}-{1}-{2}-objects.{3}", zoomLevel, x, y, extension));
164 }
165
166 private Bitmap GetInputTileImage(string fileName)
167 {
168 try
169 {
170 if (File.Exists(fileName))
171 return new Bitmap(fileName);
172 }
173 catch (Exception e)
174 {
175 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
176 }
177
178 return null;
179 }
180
181 private Bitmap GetOutputTileImage(string fileName)
182 {
183 try
184 {
185 if (File.Exists(fileName))
186 return new Bitmap(fileName);
187
188 else
189 {
190 // Create a new output tile with a transparent background
191 Bitmap bm = new Bitmap(IMAGE_WIDTH, IMAGE_WIDTH);
192 bm.MakeTransparent();
193 return bm;
194 }
195 }
196 catch (Exception e)
197 {
198 m_log.WarnFormat("[MAP IMAGE SERVICE]: Unable to read image data from {0}: {1}", fileName, e);
199 }
200
201 return null;
202 }
203
204 private bool CreateTile(uint zoomLevel, int x, int y)
205 {
206 m_log.DebugFormat("[MAP IMAGE SERVICE]: Create tile for {0} {1}, zoom {2}", x, y, zoomLevel);
207 int prevWidth = (int)Math.Pow(2, (double)zoomLevel - 2);
208 int thisWidth = (int)Math.Pow(2, (double)zoomLevel - 1);
209
210 // Convert x and y to the bottom left tile for this zoom level
211 int xIn = x - (x % prevWidth);
212 int yIn = y - (y % prevWidth);
213
214 // Convert x and y to the bottom left tile for the next zoom level
215 int xOut = x - (x % thisWidth);
216 int yOut = y - (y % thisWidth);
217
218 // Try to open the four input tiles from the previous zoom level
219 Bitmap inputBL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn));
220 Bitmap inputBR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn));
221 Bitmap inputTL = GetInputTileImage(GetFileName(zoomLevel - 1, xIn, yIn + prevWidth));
222 Bitmap inputTR = GetInputTileImage(GetFileName(zoomLevel - 1, xIn + prevWidth, yIn + prevWidth));
223
224 // Open the output tile (current zoom level)
225 string outputFile = GetFileName(zoomLevel, xOut, yOut);
226 Bitmap output = GetOutputTileImage(outputFile);
227 if (output == null)
228 return false;
229 FillImage(output, m_Watercolor);
230
231 if (inputBL != null)
232 {
233 ImageCopyResampled(output, inputBL, 0, HALF_WIDTH, 0, 0);
234 inputBL.Dispose();
235 }
236 if (inputBR != null)
237 {
238 ImageCopyResampled(output, inputBR, HALF_WIDTH, HALF_WIDTH, 0, 0);
239 inputBR.Dispose();
240 }
241 if (inputTL != null)
242 {
243 ImageCopyResampled(output, inputTL, 0, 0, 0, 0);
244 inputTL.Dispose();
245 }
246 if (inputTR != null)
247 {
248 ImageCopyResampled(output, inputTR, HALF_WIDTH, 0, 0, 0);
249 inputTR.Dispose();
250 }
251
252 // Write the modified output
253 try
254 {
255 using (Bitmap final = new Bitmap(output))
256 {
257 output.Dispose();
258 final.Save(outputFile);
259 }
260 }
261 catch (Exception e)
262 {
263 m_log.WarnFormat("[MAP IMAGE SERVICE]: Oops on saving {0} {1}", outputFile, e);
264 }
265
266 // Save also as png?
267
268 return true;
269 }
270
271 #region Image utilities
272
273 private void FillImage(Bitmap bm, Color c)
274 {
275 for (int x = 0; x < bm.Width; x++)
276 for (int y = 0; y < bm.Height; y++)
277 bm.SetPixel(x, y, c);
278 }
279
280 private void ImageCopyResampled(Bitmap output, Bitmap input, int destX, int destY, int srcX, int srcY)
281 {
282 int resamplingRateX = 2; // (input.Width - srcX) / (output.Width - destX);
283 int resamplingRateY = 2; // (input.Height - srcY) / (output.Height - destY);
284
285 for (int x = destX; x < destX + HALF_WIDTH; x++)
286 for (int y = destY; y < destY + HALF_WIDTH; y++)
287 {
288 Color p = input.GetPixel(srcX + (x - destX) * resamplingRateX, srcY + (y - destY) * resamplingRateY);
289 output.SetPixel(x, y, p);
290 }
291 }
292
293 #endregion
294 }
295}