一、ArcGIS 地图切片原理
详见上一篇文章内容。
二、java实现思路
第一步:根据输入的地图要素,获取该要素的外包矩形,计算该要素所在的行列号:
public java.awt.Rectangle getOverlappingCellsESRI(Rectangle rect) {
int col1, col2, row1, row2;
plottingMAXScale = 0.019035688046642241;
col1 = (int)Math.floor((rect.x1 - this.x1) / 256 / plottingMAXScale );
col2 = (int)Math.floor((rect.x2 - this.x1) / 256 / plottingMAXScale);
row1 = (int)Math.floor((Math.abs(rect.y1 - this.y1)) / 256 / plottingMAXScale);
row2 = (int)Math.floor((Math.abs(rect.y2 - this.y1)) / 256 / plottingMAXScale);
return new java.awt.Rectangle(col1,row2,col2-col1,row1-row2);
}
其中plottingMAXScale是指的分辨率,上一篇博客已经讲述了怎么计算了,这里测试就写死了。方法返回的是该要素所在的起始行row和起始列,已经宽度和高度。
第二步:根据行列号、宽度和高度,来进行画布的生成。
for (Shape shape : shapes) {
Rectangle shapeMBR = shape.getMBR();
if (shapeMBR == null)
continue;
java.awt.Rectangle overlappingCells = bottomGrid.getOverlappingCellsESRI(shapeMBR);
// Iterate over levels from bottom up
for (key.level = maxLevel; key.level >= minLevel; key.level--) {
for (key.y = overlappingCells.y; key.y <= overlappingCells.y + overlappingCells.height; key.y++) {
for (key.x = overlappingCells.x; key.x <= overlappingCells.x + overlappingCells.width; key.x++) {
Canvas canvas = canvases.get(key);
if (canvas == null) {
Rectangle tileMBR = new Rectangle();
tileMBR.x1 = -400 + 256 * bottomGrid.plottingMAXScale * key.x;
tileMBR.x2 = -400 + 256 * bottomGrid.plottingMAXScale * (key.x + 1);
tileMBR.y1 = 400 - 256 * bottomGrid.plottingMAXScale * (key.y + 1);
tileMBR.y2 = 400 - 256 * bottomGrid.plottingMAXScale * key.y;
canvas = plotter.createCanvas(tileWidth, tileHeight, tileMBR);
canvases.put(key.clone(), canvas);
}
plotter.plot(canvas, shape);
}
}
// Update overlappingCells for the higher level
int updatedX1 = overlappingCells.x / 2;
int updatedY1 = overlappingCells.y / 2;
int updatedX2 = (overlappingCells.x + overlappingCells.width) / 2;
int updatedY2 = (overlappingCells.y + overlappingCells.height) / 2;
overlappingCells.x = updatedX1;
overlappingCells.y = updatedY1;
overlappingCells.width = updatedX2 - updatedX1 ;
overlappingCells.height = updatedY2 - updatedY1 ;
bottomGrid.plottingMAXScale = bottomGrid.plottingMAXScale * 2;
}
}
}
画布生成过程中是从最大level开始,根据行列号来计算该要素所在切片的大小,得到canvas,然后将该要素绘制到该画布上。这里需要注意的是在计算画布外包矩形时,注意是从坐标系原点出发的,不要和Tiling scheme origin弄混淆了。
第三步:将生成的画布绘制成图片
// Write the tiles
final Entry<TileIndexESRI, Canvas>[] entries =
canvases.entrySet().toArray(new Map.Entry[canvases.size()]);
// Clear the hash map to save memory as it is no longer needed
canvases.clear();
int parallelism = params.getInt("parallel",
Runtime.getRuntime().availableProcessors());
Parallel.forEach(entries.length, new RunnableRange<Object>() {
@Override
public Object run(int i1, int i2) {
boolean output = params.getBoolean("output", true);
try {
Plotter plotter = plotterClass.newInstance();
plotter.configure(params);
for (int i = i1; i < i2; i++) {
Map.Entry<TileIndexESRI, Canvas> entry = entries[i];
TileIndexESRI key = entry.getKey();
Path imagePath = new Path(outPath + "\\" + "L" + String.format("%0"+2+"d", key.level), key.getImageFileName()+extension);
// Write this tile to an image
DataOutputStream outFile = output? outFS.create(imagePath)
: new DataOutputStream(new NullOutputStream());
plotter.writeImage(entry.getValue(), outFile, vflip);
outFile.close();
// Remove entry to allows GC to collect it
entries[i] = null;
}
return null;
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}, parallelism);
} catch (InstantiationException e) {
throw new RuntimeException("Error creating rastierizer", e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Error creating rastierizer", e);
}
以此将计算得到的canvas绘制成地图切片。以上就可以生成完美的地图切片了。我们来看下结果:
这里通过ArcGIS api for JS;
while (templateFileReader.readLine(line) > 0) {
String lineStr = line.toString();
//full extent
lineStr = lineStr.replace("#{XMIN}", Double.toString(inputMBR.x1));
lineStr = lineStr.replace("#{XMAX}", Double.toString(inputMBR.x2));
lineStr = lineStr.replace("#{YMIN}", Double.toString(inputMBR.y1));
lineStr = lineStr.replace("#{YMAX}", Double.toString(inputMBR.y2));
// level of detals information
lineStr = lineStr.replace("#{LODInfos}", lodInfoString);
htmlOut.println(lineStr);
}
对模板进行参数修改。这里主要修改了地图范围和level of details information.看一下模板:
通过以上的步骤就可以完成地图切片,我们来看下结果吧:
那么点击index.html会出现什么呢,见证奇迹的时刻;
通过以上的测试,采用java语言,根据ArcGIS Server地图切片的原理,实现了地图数据的缓存过程。完结!