我使用 Google Maps API 创建了一张 map ,突出显示了明尼苏达州的所有县。基本上,我使用一组经度/纬度坐标创建了县多边形。这是生成的 map 的截图:-
用户要求之一是能够将类似的 map 作为图像,以便他们可以将其嵌入到他们的 PowerPoint/主题幻灯片中。我找不到任何有用的 Google Maps API 可以让我按原样保存我的自定义 map (如果你知道方法,请告诉我),所以我认为我应该用 Java 中的 Graphics2D 绘制它。
在阅读了将经度/纬度转换为 X/Y 坐标的公式后,我得到了以下代码:-
private static final int EARTH_RADIUS = 6371;
private static final double FOCAL_LENGTH = 500;
...
BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bi.createGraphics();
for (Coordinate coordinate : coordinates) {
double latitude = Double.valueOf(coordinate.getLatitude());
double longitude = Double.valueOf(coordinate.getLongitude());
latitude = latitude * Math.PI / 180;
longitude = longitude * Math.PI / 180;
double x = EARTH_RADIUS * Math.sin(latitude) * Math.cos(longitude);
double y = EARTH_RADIUS * Math.sin(latitude) * Math.sin(longitude);
double z = EARTH_RADIUS * Math.cos(latitude);
double projectedX = x * FOCAL_LENGTH / (FOCAL_LENGTH + z);
double projectedY = y * FOCAL_LENGTH / (FOCAL_LENGTH + z);
// scale the map bigger
int magnifiedX = (int) Math.round(projectedX * 5);
int magnifiedY = (int) Math.round(projectedY * 5);
...
g.drawPolygon(...);
...
}
生成的 map 与 Google Maps API 使用同一组经度/纬度生成的 map 相似。但是,它似乎有点倾斜,看起来有点偏离,我不知道如何解决这个问题。
如何使县的形状看起来与上面的 Google Maps API 生成的形状一样?
非常感谢。
最终解决方案
感谢@QuantumMechanic 和@Anon,我终于找到了解决方案。
墨卡托投影在这里真的很有效。我正在使用 Java Map Projection Library执行墨卡托投影的计算。
private static final int IMAGE_WIDTH = 1000;
private static final int IMAGE_HEIGHT = 1000;
private static final int IMAGE_PADDING = 50;
...
private List<Point2D.Double> convertToXY(List<Coordinate> coordinates) {
List<Point2D.Double> xys = new ArrayList<Point2D.Double>();
MercatorProjection projection = new MercatorProjection();
for (Coordinate coordinate : coordinates) {
double latitude = Double.valueOf(coordinate.getLatitude());
double longitude = Double.valueOf(coordinate.getLongitude());
// convert to radian
latitude = latitude * Math.PI / 180;
longitude = longitude * Math.PI / 180;
Point2D.Double d = projection.project(longitude, latitude, new Point2D.Double());
// shift by 10 to remove negative Xs and Ys
// scaling by 6000 to make the map bigger
int magnifiedX = (int) Math.round((10 + d.x) * 6000);
int magnifiedY = (int) Math.round((10 + d.y) * 6000);
minX = (minX == -1) ? magnifiedX : Math.min(minX, magnifiedX);
minY = (minY == -1) ? magnifiedY : Math.min(minY, magnifiedY);
xys.add(new Point2D.Double(magnifiedX, magnifiedY));
}
return xys;
}
...
通过使用生成的 XY 坐标, map 看起来像是倒置的,那是因为我相信 graphics2D 的 0,0 从左上角开始。所以,我需要通过从图像高度中减去值来反转 Y,如下所示:-
...
Polygon polygon = new Polygon();
for (Point2D.Double point : xys) {
int adjustedX = (int) (IMAGE_PADDING + (point.getX() - minX));
// need to invert the Y since 0,0 starts at top left
int adjustedY = (int) (IMAGE_HEIGHT - IMAGE_PADDING - (point.getY() - minY));
polygon.addPoint(adjustedX, adjustedY);
}
...
这是生成的 map :-
太完美了!
2013 年 1 月 25 日更新
这是根据宽度和高度(以像素为单位)创建图像映射的代码。在这种情况下,我不依赖 Java Map Project Library,而是提取了相关公式并将其嵌入到我的代码中。与上面依赖于任意缩放值的代码示例(上面的示例使用 6000)相比,这使您可以更好地控制 map 的生成。
public class MapService {
// CHANGE THIS: the output path of the image to be created
private static final String IMAGE_FILE_PATH = "/some/user/path/map.png";
// CHANGE THIS: image width in pixel
private static final int IMAGE_WIDTH_IN_PX = 300;
// CHANGE THIS: image height in pixel
private static final int IMAGE_HEIGHT_IN_PX = 500;
// CHANGE THIS: minimum padding in pixel
private static final int MINIMUM_IMAGE_PADDING_IN_PX = 50;
// formula for quarter PI
private final static double QUARTERPI = Math.PI / 4.0;
// some service that provides the county boundaries data in longitude and latitude
private CountyService countyService;
public void run() throws Exception {
// configuring the buffered image and graphics to draw the map
BufferedImage bufferedImage = new BufferedImage(IMAGE_WIDTH_IN_PX,
IMAGE_HEIGHT_IN_PX,
BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
Map<RenderingHints.Key, Object> map = new HashMap<RenderingHints.Key, Object>();
map.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
map.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
map.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
RenderingHints renderHints = new RenderingHints(map);
g.setRenderingHints(renderHints);
// min and max coordinates, used in the computation below
Point2D.Double minXY = new Point2D.Double(-1, -1);
Point2D.Double maxXY = new Point2D.Double(-1, -1);
// a list of counties where each county contains a list of coordinates that form the county boundary
Collection<Collection<Point2D.Double>> countyBoundaries = new ArrayList<Collection<Point2D.Double>>();
// for every county, convert the longitude/latitude to X/Y using Mercator projection formula
for (County county : countyService.getAllCounties()) {
Collection<Point2D.Double> lonLat = new ArrayList<Point2D.Double>();
for (CountyBoundary countyBoundary : county.getCountyBoundaries()) {
// convert to radian
double longitude = countyBoundary.getLongitude() * Math.PI / 180;
double latitude = countyBoundary.getLatitude() * Math.PI / 180;
Point2D.Double xy = new Point2D.Double();
xy.x = longitude;
xy.y = Math.log(Math.tan(QUARTERPI + 0.5 * latitude));
// The reason we need to determine the min X and Y values is because in order to draw the map,
// we need to offset the position so that there will be no negative X and Y values
minXY.x = (minXY.x == -1) ? xy.x : Math.min(minXY.x, xy.x);
minXY.y = (minXY.y == -1) ? xy.y : Math.min(minXY.y, xy.y);
lonLat.add(xy);
}
countyBoundaries.add(lonLat);
}
// readjust coordinate to ensure there are no negative values
for (Collection<Point2D.Double> points : countyBoundaries) {
for (Point2D.Double point : points) {
point.x = point.x - minXY.x;
point.y = point.y - minXY.y;
// now, we need to keep track the max X and Y values
maxXY.x = (maxXY.x == -1) ? point.x : Math.max(maxXY.x, point.x);
maxXY.y = (maxXY.y == -1) ? point.y : Math.max(maxXY.y, point.y);
}
}
int paddingBothSides = MINIMUM_IMAGE_PADDING_IN_PX * 2;
// the actual drawing space for the map on the image
int mapWidth = IMAGE_WIDTH_IN_PX - paddingBothSides;
int mapHeight = IMAGE_HEIGHT_IN_PX - paddingBothSides;
// determine the width and height ratio because we need to magnify the map to fit into the given image dimension
double mapWidthRatio = mapWidth / maxXY.x;
double mapHeightRatio = mapHeight / maxXY.y;
// using different ratios for width and height will cause the map to be stretched. So, we have to determine
// the global ratio that will perfectly fit into the given image dimension
double globalRatio = Math.min(mapWidthRatio, mapHeightRatio);
// now we need to readjust the padding to ensure the map is always drawn on the center of the given image dimension
double heightPadding = (IMAGE_HEIGHT_IN_PX - (globalRatio * maxXY.y)) / 2;
double widthPadding = (IMAGE_WIDTH_IN_PX - (globalRatio * maxXY.x)) / 2;
// for each country, draw the boundary using polygon
for (Collection<Point2D.Double> points : countyBoundaries) {
Polygon polygon = new Polygon();
for (Point2D.Double point : points) {
int adjustedX = (int) (widthPadding + (point.getX() * globalRatio));
// need to invert the Y since 0,0 starts at top left
int adjustedY = (int) (IMAGE_HEIGHT_IN_PX - heightPadding - (point.getY() * globalRatio));
polygon.addPoint(adjustedX, adjustedY);
}
g.drawPolygon(polygon);
}
// create the image file
ImageIO.write(bufferedImage, "PNG", new File(IMAGE_FILE_PATH));
}
}
结果:图片宽度 = 600 像素,图片高度 = 600 像素,图片内边距 = 50 像素
结果:图片宽度 = 300 像素,图片高度 = 500 像素,图片内边距 = 50 像素
最佳答案
绘制 map 的最大问题是地球的球面不能方便地转换为平面表示。有许多不同的预测试图解决这个问题。
Mercator是最简单的一种:它假设等纬线是平行的水平线,而等经线是平行的垂直线。这对纬度有效(1 个纬度约等于 111 公里,无论您身在何处),但对经度无效(一个经度的表面距离与 latitutude 的余弦成正比)。
但是,只要您的温度低于 45 度(明尼苏达州的大部分地区都如此),墨卡托投影就可以很好地工作,并且可以创建大多数人会从他们的小学 map 中识别的形式。而且非常简单:只需将点视为绝对坐标,然后缩放到您要绘制它们的任何空间。无需触发。
关于java - 将经度/纬度转换为 X/Y 坐标,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5983099/
我的目标是转换表单输入,例如“100兆字节”或“1GB”,并将其转换为我可以存储在数据库中的文件大小(以千字节为单位)。目前,我有这个:defquota_convert@regex=/([0-9]+)(.*)s/@sizes=%w{kilobytemegabytegigabyte}m=self.quota.match(@regex)if@sizes.include?m[2]eval("self.quota=#{m[1]}.#{m[2]}")endend这有效,但前提是输入是倍数(“gigabytes”,而不是“gigabyte”)并且由于使用了eval看起来疯狂不安全。所以,功能正常,
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我需要读入一个包含数字列表的文件。此代码读取文件并将其放入二维数组中。现在我需要获取数组中所有数字的平均值,但我需要将数组的内容更改为int。有什么想法可以将to_i方法放在哪里吗?ClassTerraindefinitializefile_name@input=IO.readlines(file_name)#readinfile@size=@input[0].to_i@land=[@size]x=1whilex 最佳答案 只需将数组映射为整数:@land边注如果你想得到一条线的平均值,你可以这样做:values=@input[x]
这道题是thisquestion的逆题.给定一个散列,每个键都有一个数组,例如{[:a,:b,:c]=>1,[:a,:b,:d]=>2,[:a,:e]=>3,[:f]=>4,}将其转换为嵌套哈希的最佳方法是什么{:a=>{:b=>{:c=>1,:d=>2},:e=>3,},:f=>4,} 最佳答案 这是一个迭代的解决方案,递归的解决方案留给读者作为练习:defconvert(h={})ret={}h.eachdo|k,v|node=retk[0..-2].each{|x|node[x]||={};node=node[x]}node[
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我正在使用Rails构建一个简单的聊天应用程序。当用户输入url时,我希望将其输出为html链接(即“url”)。我想知道在Ruby中是否有任何库或众所周知的方法可以做到这一点。如果没有,我有一些不错的正则表达式示例代码可以使用... 最佳答案 查看auto_linkRails提供的辅助方法。这会将所有URL和电子邮件地址变成可点击的链接(htmlanchor标记)。这是文档中的代码示例。auto_link("Gotohttp://www.rubyonrails.organdsayhellotodavid@loudthinking.
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我收到格式为的回复#我需要将其转换为哈希值(针对活跃商家)。目前我正在遍历变量并执行此操作:response.instance_variables.eachdo|r|my_hash.merge!(r.to_s.delete("@").intern=>response.instance_eval(r.to_s.delete("@")))end这有效,它将生成{:first="charlie",:last=>"kelly"},但它似乎有点hacky和不稳定。有更好的方法吗?编辑:我刚刚意识到我可以使用instance_variable_get作为该等式的第二部分,但这仍然是主要问题。
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht