类似这样的例子应该是在很早之前就有接触了,同事当时展示了一个在app端绘制一个面,然后在web端能够展示出来,这过程不需要重新去发布地图服务,应该说这样的功能,在ogc服务中,就是wfs。而现在之所以回想到了之前的情景,是因为有小伙伴在openlayer中使用Geoserver发布的wfs要素服务,包了如下的错误。估计一时没有找到解决办法。
一开始,主要对了相应的参数进行检测。那么我们来看一下。我们的请求。在下面的这段代码中可以看到几个参数,需要细致的修改
//创建feature
var newFeature = new ol.Feature();
newFeature.setGeometryName('the_geom');
newFeature.setGeometry(new ol.geom.MultiPolygon([[[116.22,31.78],[117.23,31.78],[117.22,31.79],[116.22,31.78]]]));
//newFeature.setGeometry(polygon);
newFeature.set('OBJECTID',14);
newFeature.set('wg', 'sswg14');
newFeature.set('wgy', 'sswgy14');
newFeature.set('bm', 'ssbm14');
newFeature.set('wey', 'sswey14');
newFeature.set('Id', 14);
newFeature.set('SSWG', 'ssSSWG14');
newFeature.set('SSJD', 'ssSSJD14');
newFeature.set('SQMC', 'ssSQMC14');
newFeature.set('FIRST_Id',14);
newFeature.set('Shape_Leng', 14.14);
newFeature.set('Shape_Area', 14.235);
AddWfsFeature([newFeature]);
AddWfsFeature([newFeature]);
function AddWfsFeature(features) {
var WFSTSerializer = new ol.format.WFS();
var featObject = WFSTSerializer.writeTransaction(features,
null, null, {
featureType: 'lianhua',
featureNS: 'http://localhost:8088/geoserver/',
srsName: 'EPSG:4326'
});
var serializer = new XMLSerializer();
var featString = serializer.serializeToString(featObject);
var request = new XMLHttpRequest();
request.open('POST', 'http://localhost:8088/geoserver/wfs?service=wfs');
request.setRequestHeader('Content-Type', 'text/xml');
request.send(featString);
}
在Geoserver中,可以看到如下的发布图层。现在我们需要找到sde的工作空间。
切换到数据存储。如下图所示。
然后找到sde目录。下面的【命名】需要在上面请求代码的featurePrefix,【命名空间URL】则对应我们的featureNS
如果填写正确在这里,会报出空间信息错误。来看一下Geoserver是如何对工作空间进行检测的。
我们来看一下代码,对WFS工作空间查询类WFSWorkspaceQualifier类。特别需要注意的下面的qualifyRequest函数。
public class WFSWorkspaceQualifier extends WorkspaceQualifyingCallback {
public WFSWorkspaceQualifier(Catalog catalog) {
super(catalog);
}
@Override
protected void qualifyRequest(
WorkspaceInfo workspace, PublishedInfo layer, Service service, Request request) {
if (request.getContext() != null) {
// if a qualifying workspace exist, try to qualify the request typename
// parameter, if present
if (workspace != null && request.getKvp().containsKey("TYPENAME")) {
Iterable typeNames = (Iterable) request.getKvp().get("TYPENAME");
NamespaceInfo ns = catalog.getNamespaceByPrefix(workspace.getName());
if (ns != null) {
List<QName> qualifiedNames = new ArrayList<QName>();
for (Object name : typeNames) {
if (name != null && name instanceof QName) {
QName typeName = (QName) name;
// no namespace specified, we can qualify
if (typeName.getNamespaceURI() == null
|| typeName.getNamespaceURI().equals("")) {
typeName = new QName(ns.getURI(), typeName.getLocalPart());
} else if (typeName.getNamespaceURI()
.equals(catalog.getDefaultNamespace().getURI())
|| !typeName.getNamespaceURI().equals(ns.getURI())) {
// more complex case, if we have the default
// namespace, we have to check if it's been
// specified on the request, or assigned by parser
typeName = checkOriginallyUnqualified(request, ns, typeName);
}
qualifiedNames.add(typeName);
}
}
request.getKvp().put("TYPENAME", qualifiedNames);
}
}
}
}
/**
* Checks if the typeName default namespace is present in the original request, or it has been
* overridden by parser. If it's been overridden we can qualify with the given namespace.
*
* @param request
* @param ns
* @param typeName
*/
private QName checkOriginallyUnqualified(Request request, NamespaceInfo ns, QName typeName) {
Map<String, String[]> originalParams = request.getHttpRequest().getParameterMap();
for (String paramName : originalParams.keySet()) {
if (paramName.equalsIgnoreCase("TYPENAME")) {
for (String originalTypeName : originalParams.get(paramName)) {
if (originalTypeName.equals(typeName.getLocalPart())) {
// the original typeName was not
// qualified, we can qualify it
typeName = new QName(ns.getURI(), typeName.getLocalPart());
}
}
}
}
return typeName;
}
@Override
protected void qualifyRequest(
WorkspaceInfo workspace, PublishedInfo layer, Operation operation, Request request) {
NamespaceInfo ns = catalog.getNamespaceByPrefix(workspace.getName());
GetCapabilitiesRequest caps =
GetCapabilitiesRequest.adapt(
OwsUtils.parameter(operation.getParameters(), EObject.class));
if (caps != null) {
caps.setNamespace(workspace.getName());
return;
}
DescribeFeatureTypeRequest dft =
DescribeFeatureTypeRequest.adapt(
OwsUtils.parameter(operation.getParameters(), EObject.class));
if (dft != null) {
qualifyTypeNames(dft.getTypeNames(), workspace, ns);
return;
}
GetFeatureRequest gf =
GetFeatureRequest.adapt(
OwsUtils.parameter(operation.getParameters(), EObject.class));
if (gf != null) {
for (Query q : gf.getQueries()) {
// in case of stored query usage the typenames might be null
if (q.getTypeNames() != null) {
qualifyTypeNames(q.getTypeNames(), workspace, ns);
}
}
return;
}
LockFeatureRequest lf =
LockFeatureRequest.adapt(
OwsUtils.parameter(operation.getParameters(), EObject.class));
if (lf != null) {
for (Lock lock : lf.getLocks()) {
lock.setTypeName(qualifyTypeName(lock.getTypeName(), workspace, ns));
}
return;
}
TransactionRequest t =
TransactionRequest.adapt(
OwsUtils.parameter(operation.getParameters(), EObject.class));
if (t != null) {
for (TransactionElement el : t.getElements()) {
if (el instanceof Insert) {
Insert in = (Insert) el;
// in the insert case the objects are gt feature types which are not mutable
// so we just check them and throw an exception if a name does not match
List features = in.getFeatures();
ensureFeatureNamespaceUriMatches(features, ns, t);
} else if (el instanceof Replace) {
Replace rep = (Replace) el;
// in the replace case the objects are gt feature types which are not mutable
// so we just check them and throw an exception if a name does not match
List features = rep.getFeatures();
ensureFeatureNamespaceUriMatches(features, ns, t);
} else {
el.setTypeName(qualifyTypeName(el.getTypeName(), workspace, ns));
}
}
}
}
/**
* Iterates the given features and ensures their namespaceURI matches the given namespace
*
* @param features
* @param ns
* @param t
*/
private void ensureFeatureNamespaceUriMatches(
List features, NamespaceInfo ns, TransactionRequest t) {
for (Iterator j = features.iterator(); j.hasNext(); ) {
Object next = j.next();
if (next instanceof Feature) {
Feature f = (Feature) next;
Name n = f.getType().getName();
if (n.getNamespaceURI() != null && !ns.getURI().equals(n.getNamespaceURI())) {
throw new WFSException(t, "No such feature type " + n);
}
}
}
}
void qualifyTypeNames(List names, WorkspaceInfo ws, NamespaceInfo ns) {
if (names != null) {
for (int i = 0; i < names.size(); i++) {
QName name = (QName) names.get(i);
names.set(i, qualifyTypeName(name, ws, ns));
}
}
}
QName qualifyTypeName(QName name, WorkspaceInfo ws, NamespaceInfo ns) {
if (name != null) {
return new QName(ns.getURI(), name.getLocalPart(), ws.getName());
}
return null;
}
}
在qualifyRequest函数中,我们可以看到正确的工作空间和表。
我们再来看一下,使用到的函数。从这里可以看得出使用features进行了遍历,然后取出相应的工作空间来比对。
private void ensureFeatureNamespaceUriMatches(
List features, NamespaceInfo ns, TransactionRequest t) {
for (Iterator j = features.iterator(); j.hasNext(); ) {
Object next = j.next();
if (next instanceof Feature) {
Feature f = (Feature) next;
Name n = f.getType().getName();
if (n.getNamespaceURI() != null && !ns.getURI().equals(n.getNamespaceURI())) {
throw new WFSException(t, "No such feature type " + n);
}
}
}
}
而我们这里填写的是这样的工作空间。如下,显然是不对的。因为匹配不到发布的WFS要素服务的命名空间。
好了,修改好上面的工作空间后,我们再来看一下其他的参数请求。根据上面报错信息,进入到错误的提示位置。在visit方法中,出现了报错信息,大意是不能为空的字符进行数据的转换。
public void visit(Binding binding) {
Class bindingClass;
if (!(binding instanceof InstanceBinding)) {
bindingClass = binding.getClass();
QName bindingTarget = binding.getTarget();
binding = (Binding)this.context.getComponentInstanceOfType(binding.getClass());
if (binding == null) {
binding = this.parser.getBindingLoader().loadBinding(bindingTarget, this.context);
if (binding == null) {
binding = this.parser.getBindingLoader().loadBinding(bindingTarget, bindingClass, this.context);
}
if (binding.getClass() != bindingClass) {
throw new IllegalStateException("Reloaded binding resulted in different type, from " + bindingClass + " to " + binding.getClass());
}
}
}
try {
if (this.result == null) {
bindingClass = null;
XSDTypeDefinition type;
if (Schemas.nameMatches(this.instance.getDeclaration(), binding.getTarget())) {
type = this.instance.getTypeDefinition();
} else {
type = Schemas.getBaseTypeDefinition(this.instance.getTypeDefinition(), binding.getTarget());
}
if (this.value == null) {
this.value = this.preParse(this.instance);
if (type != null && (type instanceof XSDSimpleTypeDefinition || ((XSDComplexTypeDefinition)type).isMixed())) {
this.result = this.value;
} else if (this.value != null && this.value instanceof String) {
if ("".equals(((String)this.value).trim())) {
this.result = null;
} else {
this.result = this.value;
}
} else {
this.result = this.value;
}
}
}
if (binding instanceof SimpleBinding) {
this.result = ((SimpleBinding)binding).parse(this.instance, this.result);
} else {
this.result = ((ComplexBinding)binding).parse((ElementInstance)this.instance, this.node, this.result);
}
if (this.result != null) {
this.value = this.result;
}
} catch (Throwable var4) {
String msg = "Parsing failed for " + this.instance.getName() + ": " + var4.toString();
throw new RuntimeException(msg, var4);
}
}
好了,到上面的这一步就很是奇怪。因通过下面的代码可以看到能够解析到坐标系epsg:4326。
public void startElement(QName qName, Attributes attributes) throws SAXException {
List atts = new ArrayList();
int i;
for(i = 0; i < attributes.getLength(); ++i) {
String rawAttQName = attributes.getQName(i);
String prefix;
String uri;
if (rawAttQName != null) {
if (rawAttQName.startsWith("xmlns:")) {
continue;
}
if (rawAttQName.endsWith("schemaLocation")) {
prefix = "";
if (rawAttQName.indexOf(58) != -1) {
prefix = rawAttQName.substring(0, rawAttQName.indexOf(58));
}
uri = this.parser.getNamespaceSupport().getURI(prefix);
if (uri != null && uri.equals("http://www.w3.org/2001/XMLSchema-instance")) {
continue;
}
}
}
prefix = attributes.getURI(i);
uri = attributes.getLocalName(i);
QName attQName = new QName(prefix, uri);
XSDAttributeDeclaration decl = Schemas.getAttributeDeclaration(this.content, attQName);
if (decl == null && !this.parser.isStrict()) {
if (this.parser.getLogger().isLoggable(Level.FINE)) {
this.parser.getLogger().fine("Parsing unknown attribute: " + attQName);
}
decl = XSDFactory.eINSTANCE.createXSDAttributeDeclaration();
decl.setName(attQName.getLocalPart());
decl.setTargetNamespace(attQName.getNamespaceURI());
XSDSimpleTypeDefinition type = (XSDSimpleTypeDefinition)XSDUtil.getSchemaForSchema("http://www.w3.org/2001/XMLSchema").getSimpleTypeIdMap().get("string");
decl.setTypeDefinition(type);
}
if (decl != null) {
AttributeInstance att = new AttributeImpl(decl);
att.setNamespace(decl.getTargetNamespace());
att.setName(decl.getName());
att.setText(attributes.getValue(i));
atts.add(att);
} else {
this.parser.getLogger().warning("Could not find attribute declaration: " + attQName);
}
}
this.element = new ElementImpl(this.content);
this.element.setNamespace(qName.getNamespaceURI());
this.element.setName(qName.getLocalPart());
this.element.setAttributes((AttributeInstance[])((AttributeInstance[])atts.toArray(new AttributeInstance[atts.size()])));
this.node = new NodeImpl(this.element);
for(i = 0; i < this.element.getAttributes().length; ++i) {
AttributeInstance attribute = this.element.getAttributes()[i];
ParseExecutor executor = new ParseExecutor(attribute, (Node)null, this.parent.getContext(), this.parser);
this.parser.getBindingWalker().walk(attribute.getAttributeDeclaration(), executor, this.parent.getContext());
Object parsed = executor.getValue();
this.node.addAttribute(new NodeImpl(attribute, parsed));
}
ElementInitializer initer = new ElementInitializer(this.element, this.node, this.parent.getContext());
this.parser.getBindingWalker().walk(this.element.getElementDeclaration(), initer, this.container(), this.parent.getContext());
this.setContext(new DefaultPicoContainer(this.parent.getContext()));
((BindingFactoryImpl)this.parser.getBindingFactory()).setContext(this.getContext());
this.parent.startChildHandler(this);
}
来看一下请求的结果。
走到这一步后,我们来看一下我们的请求。
对应的xml文件为。解析到epsg:4326就出错了。不过很是奇怪的是,这个请求中居然没有坐标信息,那么我们会不是我们的构建geometry对象出现了问题。
来看一下构建的geometry,其是一个MultiPolygon,而我们写的代码为:
newFeature.setGeometry(new ol.geom.MultiPolygon([[[116.22,31.78],[117.23,31.78],[117.22,31.79],[116.22,31.78]]]));
后面经过反复比较,哦,找到了问题了。由于小伙伴的粗心,没有正确的构造出geometry。正确的方式为。注意外部多一组中括号。
newFeature.setGeometry(new ol.geom.MultiPolygon([[[[116.22,31.78],[117.23,31.78],[117.22,31.79],[116.22,31.78]]]]));
这下我们再次来看一下请求信息。
但是,没有正确,在上面的请求中范围中,排布的方式为纬度、经度的方式。相应的,我们要做调整。最后经过请求后,
我们可以看到这样的数据。下面的这条即为我们新填的数据。
好了,我们来看一下新添加的wfs服务吧
更多内容,请微信扫二维码关注公众号,或者加入Geoserver技术交流群:1019869405