Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds function that inserts missing namespace into KML #5860

Merged
merged 27 commits into from
Oct 18, 2017

Conversation

njrivera
Copy link
Contributor

This is based on an issue with some KML files having a 'xsi:schemaLocation' attribute in the Document tag without declaring the xsi namespace, causing the load to fail. The fix, so far, has been to edit the actual file manually, but many users/customers do not know how to. This function has a map with the key as the namespace as it would be used in the file ('xsi:') and a value as a 2 element array: element 0 contains the beginning of the namespace declaration ('xmlns:xsi=') and element 1 contains the urls for the declaration. This map can be updated with other namespaces that commonly cause the same issue. The new file - undeclaredNamespaces.kml includes this same issue and is used for the test.

@cesium-concierge
Copy link

Welcome to the Cesium community @nrivera-Novetta!

Can you please send in a Contributor License Agreement (CLA) so that we can review and merge this pull request?

I noticed that CHANGES.md has not been updated. If this change updates the public API in any way, fixes a bug, or makes any non-trivial update, please add a bullet point to CHANGES.md and comment on this pull request so we know it was updated. For more info, see the Pull Request Guidelines.

I am a bot who helps you make Cesium awesome! Thanks again.

@pjcozzi
Copy link
Contributor

pjcozzi commented Oct 2, 2017

Thanks for the pull request, @nrivera-Novetta!

Do you work for Novetta and did you write this code for them? If so, we already have a CLA from Novetta that covers you.

@pjcozzi
Copy link
Contributor

pjcozzi commented Oct 2, 2017

@nrivera-Novetta please add yourself to CONTRIBUTORS.md.

@pjcozzi
Copy link
Contributor

pjcozzi commented Oct 2, 2017

@nrivera-Novetta please update CHANGES.md.

@pjcozzi
Copy link
Contributor

pjcozzi commented Oct 2, 2017

@tfili can you review this?

@pjcozzi pjcozzi requested a review from tfili October 2, 2017 12:33
@ggetz ggetz mentioned this pull request Oct 2, 2017
@@ -255,6 +255,23 @@ define([
return deferred.promise;
}

function insertNamespaces(text) {
var namespaceMap = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be cleaner to have the following then build the search strings in the loop.

var namespaceMap = {
  xsi : 'http://www.w3.org/2001/XMLSchema-instance'
};

var firstPart;
var lastPart;

for (var key in namespaceMap){
Copy link
Contributor

@tfili tfili Oct 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XML doesn't allow for whitespace between < and the tag name, so you could check for <xsi:, so you don't inadvertantly match soemthing like <exsi:name>. In the loop you could check for a bad file like this

var prefix = '<' + key + ':';
var declaration = 'xmlns:' + key + '=';
if(text.indexOf(prefix) !== -1 && text.indexOf(declaration) === -1) {


for (var key in namespaceMap){
if(text.indexOf(key) !== -1 && text.indexOf(namespaceMap[key][0]) === -1) {
firstPart = text.substr(0, text.indexOf('<kml') + 4);
Copy link
Contributor

@tfili tfili Oct 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can find the index of <kml once before the loop, then you can just append to the firstPart as you iterate, then combine firstPart and lastPart after the loop.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You still only want to find <kml once, but you may want to do it in the loop after we found a problem, so we don't search a potentially long string if we don't need to. So something like this

var firstPart;
var lastPart;
for (var key in namespaceMap){
...
  if(text.indexOf(prefix) !== -1 && text.indexOf(declaration) === -1) {
    if (!defined(firstPart)) {
      // Search for <kml and set firstPart/lastPart
    }
    ...
  }
}

if (defined(firstPart)) {
  text = firstPart + lastPart;
}

return text;

@njrivera njrivera closed this Oct 9, 2017
@njrivera
Copy link
Contributor Author

@tfili with regard to what you said about xml not allowing for a space between a < and a tag, there is a special case that is identical to the one in my test kml in which the 'xsi:' is an attribute rather than a tag. It seems that there are a bunch of kml's floating around with this same xsi attribute and without any xsi tags in the document. I'm going to change the code to account for this and for what you mentioned and put the PR back up.

@njrivera njrivera reopened this Oct 10, 2017
CHANGES.md Outdated
@@ -18,6 +18,7 @@ Change Log
* Fixed a 3D Tiles point cloud bug causing a stray point to appear at the center of the screen on certain hardware. [#5599](https://github.com/AnalyticalGraphicsInc/cesium/issues/5599)
* Fixed removing multiple event listeners within event callbacks. [#5827](https://github.com/AnalyticalGraphicsInc/cesium/issues/5827)
* Running `buildApps` now creates a built version of Sandcastle which uses the built version of Cesium for better performance.
* Added function that inserts missing namespace declarations into KML files. [#5860](https://github.com/AnalyticalGraphicsInc/cesium/pull/5860)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1.38 Came out already. Needs to be updated for 1.39 which will be released Nov 1.


for (var key in namespaceMap) {
if (namespaceMap.hasOwnProperty(key)) {
reg = RegExp('[<| ]' + key + ':');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ ] specifies a character group where it represents any of the characters in it. This one will match <xsi:, xsi: or |xsi:. I think you were trying to use the OR syntax that you need in capture group (represented with ( )). So it can either be [< ] or (<| ). I think just removing the | from your code and going with the character group is the way to go as it doesn't have the overhead of capturing.

@njrivera
Copy link
Contributor Author

njrivera commented Oct 18, 2017

@tfili hey just wondering if you could take another look at this

@tfili
Copy link
Contributor

tfili commented Oct 18, 2017

I'll take a look. @nrivera-Novetta, you need to add a comment when it is ready for another look or I won't be notified.

Copy link
Contributor

@tfili tfili left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides the few small tweaks, this looks good.

Also, you don't have to close a PR to work on it. We won't merge it till all the tests pass and the comments have been addressed. Just comment when its ready.

Thanks again @nrivera-Novetta

reg = RegExp('[< ]' + key + ':');
declaration = 'xmlns:' + key + '=';
if (reg.test(text) && text.indexOf(declaration) === -1) {
if (!firstPart) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be

if (!defined(firstPart)) {

Copy link
Contributor Author

@njrivera njrivera Oct 18, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tfili do you mean this line should actually use 'defined'? or just check if it's defined?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defined is a function in Cesium. It does some extra checking to make sure a value is actually undefined or null. This should be used in cases like this.

The magic of javascript's implicit type conversion would cause !firstpart to be true in other cases.

}
}

if (firstPart) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use defined here as well.

@pjcozzi
Copy link
Contributor

pjcozzi commented Oct 18, 2017

Thanks again @nrivera-Novetta!

@tfili is this ready to merge? If so, please go ahead.

@tfili tfili merged commit 9b912da into CesiumGS:master Oct 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants