从地理服务器获取图层特征信息(Google 地图)
Getting Layer Feature Information from GeoServer (Google Maps)
我正在使用 Android Studio(使用 Java)编写一个 Android 应用程序。该应用程序正在使用 Google 地图,并且有一个图层,其中包含从地理服务器获取的字段所有权信息。设置它的代码如下,并且运行良好。
public class App1Step1 extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app1_step1);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
TileProvider tileProvider = TileProviderFactory.getTileProvider();
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
}
}
class TileProviderFactory {
private static final String GEOSERVER_FORMAT =
"http://xxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetMap" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&bbox=%f,%f,%f,%f" +
"&width=256" +
"&height=256" +
"&srs=EPSG:900913" +
"&format=image/png" +
"&transparent=true";
static TileProvider getTileProvider() {
TileProvider tileProvider = new WMSTileProvider(256,256) {
@Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
double[] bbox = getBoundingBox(x, y, zoom);
String s = String.format(Locale.US, GEOSERVER_FORMAT, bbox[MINX], bbox[MINY], bbox[MAXX], bbox[MAXY]);
URL url = null;
try {
url = new URL(s);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
};
return tileProvider;
}
}
abstract class WMSTileProvider extends UrlTileProvider {
// Web Mercator n/w corner of the map.
private static final double[] TILE_ORIGIN = {-20037508.34789244, 20037508.34789244};
//array indexes for that data
private static final int ORIG_X = 0;
private static final int ORIG_Y = 1; // "
// Size of square world map in meters, using WebMerc projection.
private static final double MAP_SIZE = 20037508.34789244 * 2;
// array indexes for array to hold bounding boxes.
protected static final int MINX = 0;
protected static final int MAXX = 1;
protected static final int MINY = 2;
protected static final int MAXY = 3;
// Construct with tile size in pixels, normally 256, see parent class.
public WMSTileProvider(int x, int y) {
super(x, y);
}
// Return a web Mercator bounding box given tile x/y indexes and a zoom
// level.
protected double[] getBoundingBox(int x, int y, int zoom) {
double tileSize = MAP_SIZE / Math.pow(2, zoom);
double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
double maxx = TILE_ORIGIN[ORIG_X] + (x+1) * tileSize;
double miny = TILE_ORIGIN[ORIG_Y] - (y+1) * tileSize;
double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;
double[] bbox = new double[4];
bbox[MINX] = minx;
bbox[MINY] = miny;
bbox[MAXX] = maxx;
bbox[MAXY] = maxy;
return bbox;
}
}
上面的代码将在 Google 地图上生成以下叠加层。在地理服务器上,图层保存为 EPSG:31287 (Austria Lambert).
我现在想做的是当我点击其中一个字段时获取特征信息。我尝试了很多,但无法弄清楚如何计算必要的值(宽度、高度、x、y、bbox)以从我单击的纬度和经度获取要素信息。具体来说,这些是我遗漏的查询值。
String url = "http://xxxxxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetFeatureInfo" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&query_layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&exceptions=application/vnd.ogc.se_inimage" +
"&x=" + // ????????
"&y=" + // ????????
"&bbox=" + // ????????
"&width=" + // ????????
"&height=" + // ????????
"&srs=EPSG:900913" + // EPSG:900913 or EPSG:31287 ?
"&format=image/png" +
"&info_format=application/json" +
"&transparent=true" +
"&feature_count=50";
到目前为止我所做的(我认为)是从纬度、经度和缩放级别计算我点击的位置的 x 和 y 位置。
private void getFeatureInfo(LatLng latLng) {
// get current zoom
int zoom = (int)mMap.getCameraPosition().zoom;
// get "click" point coordinates in pixels
long pointNorthWestX = lonToX(latLng.longitude, zoom);
long pointNorthWestY = latToY(latLng.latitude, zoom);
}
public static long lonToX(double lon, int zoom) {
int offset = 256 << (zoom - 1);
return (int)Math.floor(offset + (offset * lon / 180));
}
public static long latToY(double lat, int zoom) {
int offset = 256 << (zoom - 1);
return (int)Math.floor(offset - offset / Math.PI * Math.log((1 + Math.sin(Math.toRadians(lat))) / (1 - Math.sin(Math.toRadians(lat)))) / 2);
}
不幸的是,与我在地理服务器上的图层预览中单击相同位置时获得的值相比,这些值似乎相差甚远。
有谁知道从纬度、经度和缩放级别得到我点击的位置的地物信息需要哪些计算步骤?如果对此有任何帮助,我将不胜感激。
A getFeatureRequest
本质上是 getMap
请求,用于获取您正在查询的地图并添加了一些参数。您必须添加 QUERY_LAYERS
这是您想要了解的层的名称,以及 X
& Y
(或 I
& J
在版本 1.3.0 ) 这是您想要了解的像素的位置(因此您点击的位置)。或者,您可以添加一个 info_format
来控制返回的格式。
有关详细信息,我建议您阅读 standard document。
我已经找到了解决方案,而且比我想象的要容易得多。地理服务器已经支持从我点击地图获得的 LatLon 值到 getFeatureInfo()
请求所需的 XY 坐标的投影转换。
我已将代码更改为以下内容。
private void getFeatureInfo(LatLng latLng) {
String url = "http://xxxxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetFeatureInfo" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&query_layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&exceptions=application/vnd.ogc.se_inimage" +
"&x=128" +
"&y=128" +
"&bbox=" + (latLng.longitude - 0.0000000001) + "," + (latLng.latitude - 0.0000000001) + "," + (latLng.longitude + 0.0000000001) + "," + (latLng.latitude + 0.0000000001) +
"&width=256" +
"&height=256" +
"&srs=EPSG:4326" +
"&format=image/png" +
"&info_format=application/json" +
"&transparent=true" +
"&feature_count=50";
}
width
和height
都是256
,这是我在TileProvider中初始设置的。 x
和 y
值都是 128
,这将是每个图块的中心。
我现在基本上需要做的是创建一个边界框,地理服务器将在其中查找要素。我把它做得尽可能小(应该在厘米或毫米范围内)所以只有 1 个特征是 returned。为此,我将 0.0000000001
从 minX
和 minY
值中分散注意力,并将 0.0000000001
添加到 maxX
和 maxY
值中。
重要的部分是对这个请求使用 EPSG:4326
。然后,地理服务器将自动计算 EPSG:31287
层的投影和 return 该点的特征信息。
我正在使用 Android Studio(使用 Java)编写一个 Android 应用程序。该应用程序正在使用 Google 地图,并且有一个图层,其中包含从地理服务器获取的字段所有权信息。设置它的代码如下,并且运行良好。
public class App1Step1 extends FragmentActivity implements OnMapReadyCallback {
private GoogleMap mMap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app1_step1);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
@Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
TileProvider tileProvider = TileProviderFactory.getTileProvider();
mMap.addTileOverlay(new TileOverlayOptions().tileProvider(tileProvider));
}
}
class TileProviderFactory {
private static final String GEOSERVER_FORMAT =
"http://xxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetMap" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&bbox=%f,%f,%f,%f" +
"&width=256" +
"&height=256" +
"&srs=EPSG:900913" +
"&format=image/png" +
"&transparent=true";
static TileProvider getTileProvider() {
TileProvider tileProvider = new WMSTileProvider(256,256) {
@Override
public synchronized URL getTileUrl(int x, int y, int zoom) {
double[] bbox = getBoundingBox(x, y, zoom);
String s = String.format(Locale.US, GEOSERVER_FORMAT, bbox[MINX], bbox[MINY], bbox[MAXX], bbox[MAXY]);
URL url = null;
try {
url = new URL(s);
} catch (MalformedURLException e) {
throw new AssertionError(e);
}
return url;
}
};
return tileProvider;
}
}
abstract class WMSTileProvider extends UrlTileProvider {
// Web Mercator n/w corner of the map.
private static final double[] TILE_ORIGIN = {-20037508.34789244, 20037508.34789244};
//array indexes for that data
private static final int ORIG_X = 0;
private static final int ORIG_Y = 1; // "
// Size of square world map in meters, using WebMerc projection.
private static final double MAP_SIZE = 20037508.34789244 * 2;
// array indexes for array to hold bounding boxes.
protected static final int MINX = 0;
protected static final int MAXX = 1;
protected static final int MINY = 2;
protected static final int MAXY = 3;
// Construct with tile size in pixels, normally 256, see parent class.
public WMSTileProvider(int x, int y) {
super(x, y);
}
// Return a web Mercator bounding box given tile x/y indexes and a zoom
// level.
protected double[] getBoundingBox(int x, int y, int zoom) {
double tileSize = MAP_SIZE / Math.pow(2, zoom);
double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
double maxx = TILE_ORIGIN[ORIG_X] + (x+1) * tileSize;
double miny = TILE_ORIGIN[ORIG_Y] - (y+1) * tileSize;
double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;
double[] bbox = new double[4];
bbox[MINX] = minx;
bbox[MINY] = miny;
bbox[MAXX] = maxx;
bbox[MAXY] = maxy;
return bbox;
}
}
上面的代码将在 Google 地图上生成以下叠加层。在地理服务器上,图层保存为 EPSG:31287 (Austria Lambert).
我现在想做的是当我点击其中一个字段时获取特征信息。我尝试了很多,但无法弄清楚如何计算必要的值(宽度、高度、x、y、bbox)以从我单击的纬度和经度获取要素信息。具体来说,这些是我遗漏的查询值。
String url = "http://xxxxxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetFeatureInfo" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&query_layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&exceptions=application/vnd.ogc.se_inimage" +
"&x=" + // ????????
"&y=" + // ????????
"&bbox=" + // ????????
"&width=" + // ????????
"&height=" + // ????????
"&srs=EPSG:900913" + // EPSG:900913 or EPSG:31287 ?
"&format=image/png" +
"&info_format=application/json" +
"&transparent=true" +
"&feature_count=50";
到目前为止我所做的(我认为)是从纬度、经度和缩放级别计算我点击的位置的 x 和 y 位置。
private void getFeatureInfo(LatLng latLng) {
// get current zoom
int zoom = (int)mMap.getCameraPosition().zoom;
// get "click" point coordinates in pixels
long pointNorthWestX = lonToX(latLng.longitude, zoom);
long pointNorthWestY = latToY(latLng.latitude, zoom);
}
public static long lonToX(double lon, int zoom) {
int offset = 256 << (zoom - 1);
return (int)Math.floor(offset + (offset * lon / 180));
}
public static long latToY(double lat, int zoom) {
int offset = 256 << (zoom - 1);
return (int)Math.floor(offset - offset / Math.PI * Math.log((1 + Math.sin(Math.toRadians(lat))) / (1 - Math.sin(Math.toRadians(lat)))) / 2);
}
不幸的是,与我在地理服务器上的图层预览中单击相同位置时获得的值相比,这些值似乎相差甚远。
有谁知道从纬度、经度和缩放级别得到我点击的位置的地物信息需要哪些计算步骤?如果对此有任何帮助,我将不胜感激。
A getFeatureRequest
本质上是 getMap
请求,用于获取您正在查询的地图并添加了一些参数。您必须添加 QUERY_LAYERS
这是您想要了解的层的名称,以及 X
& Y
(或 I
& J
在版本 1.3.0 ) 这是您想要了解的像素的位置(因此您点击的位置)。或者,您可以添加一个 info_format
来控制返回的格式。
有关详细信息,我建议您阅读 standard document。
我已经找到了解决方案,而且比我想象的要容易得多。地理服务器已经支持从我点击地图获得的 LatLon 值到 getFeatureInfo()
请求所需的 XY 坐标的投影转换。
我已将代码更改为以下内容。
private void getFeatureInfo(LatLng latLng) {
String url = "http://xxxxxxxxxx.at/geoserver/wms" +
"?service=WMS" +
"&version=1.1.1" +
"&request=GetFeatureInfo" +
"&layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&query_layers=satgrass:INSPIRE_SCHLAEGE_2020_POLYGON" +
"&exceptions=application/vnd.ogc.se_inimage" +
"&x=128" +
"&y=128" +
"&bbox=" + (latLng.longitude - 0.0000000001) + "," + (latLng.latitude - 0.0000000001) + "," + (latLng.longitude + 0.0000000001) + "," + (latLng.latitude + 0.0000000001) +
"&width=256" +
"&height=256" +
"&srs=EPSG:4326" +
"&format=image/png" +
"&info_format=application/json" +
"&transparent=true" +
"&feature_count=50";
}
width
和height
都是256
,这是我在TileProvider中初始设置的。 x
和 y
值都是 128
,这将是每个图块的中心。
我现在基本上需要做的是创建一个边界框,地理服务器将在其中查找要素。我把它做得尽可能小(应该在厘米或毫米范围内)所以只有 1 个特征是 returned。为此,我将 0.0000000001
从 minX
和 minY
值中分散注意力,并将 0.0000000001
添加到 maxX
和 maxY
值中。
重要的部分是对这个请求使用 EPSG:4326
。然后,地理服务器将自动计算 EPSG:31287
层的投影和 return 该点的特征信息。