I have a simple web mapping app that tries to add points to a map via a WFS-T layer with openlayers 3. For the backend I'm using geoserver v2.8.0 and a postGIS database. I've been following the great tutorial from dbauszus and I've already read this question on stack overflow, which is pointing me into the right direction but I m still missing some point.
It's kind of strange because when i add a point to the map it get's transferred into the postGIS db, a ID is set and the attribute "test" is set but the geometry column is empty. I'm assuming something with my transferred xml is not right but i can't figure out what.
Ahh don't know if it helps but CORS is enabled on my geoserver and i'm running the html site on a local web server.
Currently my code looks like this:
var formatWFS = new ol.format.WFS();
var formatGML = new ol.format.GML({
featureNS: 'vm',
featurePrefix:'vm',
featureType: 'GT_Projects',
srsName: 'EPSG:3857'
});
var transactWFS = function(p,f) {
switch(p) {
case 'insert':
node = formatWFS.writeTransaction([f],null,null,formatGML);
console.log(node);
break;
case 'update':
node = formatWFS.writeTransaction(null,[f],null,formatGML);
break;
case 'delete':
node = formatWFS.writeTransaction(null,null,[f],formatGML);
break;
}
s = new XMLSerializer();
str = s.serializeToString(node);
console.log(str);
$.ajax('http://geoserver-vmarquar.rhcloud.com:80/wfs',{
type: 'POST',
contentType: 'text/xml',
dataType: 'xml',
processData: false,
data: str
}).done();
}
$('.btnMenu').on('click', function(event) {
$('.btnMenu').removeClass('orange');
$(this).addClass('orange');
map.removeInteraction(interaction);
select.getFeatures().clear();
map.removeInteraction(select);
switch($(this).attr('id')) {
// DRAW POINT
case 'btnDrawPoint':
interaction = new ol.interaction.Draw({
type: 'Point',
source: layerVector.getSource()
});
map.addInteraction(interaction);
interaction.on('drawend', function(e) {
var feature = e.feature;
feature.set('name', "test");
// feature.set('geom', feature.getGeometry());
transactWFS('insert',feature);
});
break;
Thanks in advance!
Related
I have a web server hosting over localhost. The website I am accessing is a "Todo list app" written with AngularJS. To load the todo's, the browser gets a JSON file with the information. An example of this:
[
{"name":"Clean the house"},
{"name":"Water the dog"},
{"name":"Feed the lawn"},
{"name":"Pay dem bills"},
{"name":"Run"},
{"name":"Swim"}
]
It then loops through all the items and "prints" them out onto the website. I have various options like "Save" and "Delete". They work client-side, but that way does not allow me to properly save them, as when the browser is refreshed, all the content is reset with the server's static JSON file.
I was wondering if there was some way of using NodeJs to host the website and listen for incoming AJAX request and edit the content in the file based off that.
Writing a file asynchronously in nodejs can be done as follows.
var fs = require('fs');
var fileName = './file.json';
var file = require(fileName);
file.key = "new value"; // This will be coming as a http POST method from your view
fs.writeFile(fileName, JSON.stringify(file), function (err) {
if (err) return console.log(err);
console.log(JSON.stringify(file));
console.log('writing to ' + fileName);
});
The caveat is that json is written to the file on one line and not prettified. ex:
{
"key": "value"
}
will be...
{"key": "value"}
To avoid this, simply add these two extra arguments to JSON.stringify
JSON.stringify(file, null, 2)
null - represents the replacer function. (in this case we don't want to alter the process)
2 - represents the spaces to indent.
NodeJS does not persist data out of the box.
You want something like NodeJS + Express and special CRUD routes like POST for creating items or DELETE for deleting them.
In this routes you have to add a data persistance layer like mongoose if you want to use MongoDB or Sequelize if you want to add a SQL database behind it.
Each of this ORM requires to specify a Datamodel which can be saved.
Here an example for a mongoose implementation:
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var plugins = require('./model.server.plugins');
/**
* Customer Schema
*/
var CustomerSchema = new Schema({
name: {
type: String,
default: '',
required: 'Please fill a name',
trim: true
},
created: {
type: Date,
default: Date.now
}
});
mongoose.model('Customer', CustomerSchema);
Here is the controller
'use strict';
/**
* Module dependencies.
*/
var mongoose = require('mongoose'),
errorHandler = require('./errors.server.controller'),
Customer = mongoose.model('Customer'),
_ = require('lodash');
/**
* Create a Customer
*/
exports.create = function(req, res) {
var customer = new Customer(req.depopulated);
customer.user = req.user;
customer.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.jsonp(customer);
}
});
};
Here the route
router.route('/')
.post(customers.create);
I am using Flow.JS http://flowjs.github.io/ng-flow/ for file upload.
My requirement is such that I will have to send the following data all in one save button click
multiple files
two string values alongwith the files.
The following way works fine.
Upload ajax call
$scope.UploadFiles = function (flows) {
var data = new FormData();
$.each(flows.files, function (i, flowfile) {
data.append('file' + i, flowfile.file);
});
data.append('message', $scope.Subject);
data.append('subject', $scope.Message);
$.ajax({
url: 'url\savedata',
data: files,
cache: false,
contentType: false,
processData: false,
type: 'POST'
});
}
And my MVC conroller
public JsonResult Savedata()
{
var httpRequest = System.Web.HttpContext.Current.Request;
if(httpRequest.Files.Count != 0)
{
var collection = 0;
foreach (string file in httpRequest.Files)
{
//manipulate file data
}
}
var message = httpRequest.Forms['message'];
var subject= httpRequest.Forms['subject'];
}
All this works fine. I want to know if there is a better way to do this instead of using form data and possibly send all this data using a data model instead, since I need that for some MVC data validations.
Apologies for how trivial this may be and formatting. Link to package github: https://github.com/CollectionFS
I'm following the Store a File From the Server example.
In lib/collections.js I have:
Images = new FS.Collection("images", {
stores: [new FS.Store.FileSystem("imagest", {path: "~/uploads"})]
});
In server/test.js:
var myText = 'Hello world, I wrote this..:)';
var buffer = Buffer(myText.length);
for (var i = 0; i < myText.length; i++) {
buffer[i] = myText.charCodeAt(i);
}
Images.storeBuffer('serverFile.txt', buffer, {
// Set a contentType (optional)
contentType: 'text/plain',
noProgress: false,
// Attach custom data to the file
metadata: { text: 'some stuff' },
// Set encoding (optional default 'utf-8')
encoding: 'utf-8'
});
The error is thrown at the storeBuffer call. I've tried a few variations of saving a file from a server but I would really like to use this package.
Thanks.
This source states that the method storeBuffer should be available or FS.File.setDataFromBuffer in the devel branch, but both don't work for me.
But I found a solution by reading the source-code of FS.File:
file = new FS.File()
file.attachData buffer, {type: "text/plain"}, ->
Images.insert file
I'm trying to make ExtJs work with backend running WCF RIA services with JSON endpoint enabled. The backend I have uses GetXXX for read data and CommitChanges for create/update/delete data. It also has not ExtJs standard message format, so I have store class defined like this:
function cloneObject(src, dst) {
for (var key in src)
{
dst[key] = src[key];
}
return dst;
}
Ext.define('MyApp.store.Items', {
extend: 'Ext.data.Store',
model: 'MyApp.model.Tax',
autoLoad: true,
autoSync: true,
proxy: {
type: 'ajax',
api: {
read: '/MyApp/MyAppWeb-Web-MyAppDomain.svc/JSON/GetItems',
update: '/MyApp/MyAppWeb-Web-MyAppDomain.svc/JSON/SubmitChanges',
create: '/MyApp/MyAppWeb-Web-MyAppDomain.svc/JSON/SubmitChanges',
destroy: '/MyApp/MyAppWeb-Web-MyAppDomain.svc/JSON/SubmitChanges'
},
reader: {
type: 'json',
root: 'GetItemsResult.RootResults',
successProperty: null,
totalProperty: 'GetItemsResult.TotalCount'
},
writer: {
type: 'json',
root: 'changeSet',
currentOperation: null,
getRecordData: function(record) {
var changeSet = [];
var entity = {
Id: 0,
Operation: 3,
Entity: {
__type: 'Items:#MyApp.Web'
},
OriginalEntity: {
__type: 'Items:#MyApp.Web'
}
};
cloneObject(record.data, entity.Entity);
cloneObject(record.raw, entity.OriginalEntity);
changeSet.push(entity);
return changeSet;
}
}
}
});
As you can in order to accomodate Microsoft JSON endpoint format I had to override getRecordData and create custom JSON object. I can probably replace cloneObject function with merge function, right? (I'm still kind of new to ExtJs, so may be I'm trying to "invent a bicycle" here.
It works more or less as expected for update, however for create and delete I need to create slightly different message format. Different Operation code and no need to send OriginalEntity. However inside getRecordData I don't have information about what kind of operation is being performed. So question #1
What is the best approach here? Override 'write' method as well or is there another way?
Question #2. After any update standard store class would call reader in order to parse response, but response for update is very different then response for GetItems and I have no idea how to handle that.
Any suggestions or links to walk-through on how to tie ExtJs and Domain Services?
I ended up re-writing Proxy class to add support for different parsing for read/write operations. Works pretty well. Let me know if somebody else faces same problems - I will post code samples.
I'm building some models to interact with an existing API from a previous project.
The API relies on standard POST methods to save the data.
I've configured a model and proxy up to the point where it does push the data onto the server but there only seems to be two writer types, json & xml.
proxy: {
/* ... */
reader: {
type: 'json',
root: 'results'
},
writer: {
type: '???' // <-- can only see json or xml in the docs
}
}
Isn't there a standard POST writer that simply submits data in post fields?
I'm surprised that wouldn't be a standard writer type.
(Parsing the json format wouldn't be too hard to implement but that would mean updating a lot of the old api files.)
Ok, I was able to create that writer quite easily by checking the existing writers' source code.
One thing those existing writers are able to do - and that may be why the dev team only implemented a json and xml version - is that they can push multiple records at once.
That could be implemented in POST but would be a bit more complicated.
This writer will work if you're trying to push a single model to an api using POST:
Ext.define('Ext.data.writer.SinglePost', {
extend: 'Ext.data.writer.Writer',
alternateClassName: 'Ext.data.SinglePostWriter',
alias: 'writer.singlepost',
writeRecords: function(request, data) {
request.params = data[0];
return request;
}
});
and the use this for the writer in the proxy:
writer: {
type: 'singlepost'
}
Based on Ben answer I've implemented my own writer that will collect all properties of all models into arrays.
For example if you have model like with some fields:
fields:[
{name:'id', type:'int'}
{name:'name', type:'string'}
{name:'age', type:'date'}
]
A request string will be
id=1&id=2&id=...&name=oleks&name=max&name=...&age=...
Code:
Ext.define('Ext.data.writer.SinglePost', {
extend: 'Ext.data.writer.Writer',
alternateClassName: 'Ext.data.SinglePostWriter',
alias: 'writer.singlepost',
writeRecords: function(request, data) {
if(data && data[0]){
var keys = [];
for(var key in data[0]){
keys.push(key);
}
for(var i=0;i<keys.length;i++){
request.params[keys[i]] = [];
for(var j=0;j<data.length;j++){
request.params[keys[i]].push((data[j])[keys[i]]);
}
}
}
return request;
}
});
For Sencha touch 2.0, change the writeRecords method to:
writeRecords: function (request, data) {
var params = request.getParams() || {};
Ext.apply(params, data[0]);
request.setParams(params);
return request;
}
Here's my version, adapted from answers above:
// Subclass the original XmlWriter
Ext.define('MyApp.utils.data.writer.XmlInAPostParameter', {
extend : 'Ext.data.writer.Xml',
// give it an alias to use in writer 'type' property
alias : 'writer.xml_in_a_post_parameter',
// override the original method
writeRecords : function(request, data) {
// call the overriden method - it will put the data that I
// want into request.xmlData
this.callParent(arguments);
// copy the data in request.xmlData. In this case the XML
// data will always be in the parameter called 'XML'
Ext.apply(request.params, {
XML: request.xmlData
});
// Already copied the request payload and will not send it,
// so we delete it from the request
delete request.xmlData;
// return the modified request object
return request;
}
});
Ext.define("MyApp.model.MyModel", {
extend : "Ext.data.Model",
requires : [
'MyApp.utils.data.writer.XmlInAPostParameter'
],
fields : [ 'field_A', 'field_B' ],
proxy : {
type : 'ajax',
api : {
read : '/mymodel/read.whatever',
update : '/mymodel/write.whatever'
},
reader : {
type : 'xml'
},
writer : {
// use the alias we registered before
type : 'xml_in_a_post_parameter'
}
}
});