This is a going to be a long shot, but I am looking for advice or website material anything to help with an issue I have. This is a stormwater ArcGIS Pro issue.
The issue:
I need to create two new fields (Up_stream and Down_stream nodes) in our established inventory list of stormwater pipe infrastructure. The two new fields (which I have created) in our Pipe layer, needs to be populated to indicate which stormwater infrastructure the pipes are flowing to and from.
EX. Inlet X (up steam flows to) ------Pipe-----> Manhole Y or Inlet Z (down stream flows from)
I need to find a way to populate into the two new fields the feature IDs for the To (downstream) and From (Upstream) stormwater infrastructures.
Research question:
Does anyone have any insight and how to tackle this situation or have ideas where I can look up tutorials etc.
Has anyone had experience with this? I’m sure we are not the only one out to encounter this type of issue.
Worst case I manually input the feature ID for each pipe segment, but I am sure there must be something to help jumpstart the process.
I think we're talking slightly tangential things, here:
What linkage IDs are worth having
How to update linkage IDs for new features, as they're created
How to populate linkage IDs for existing features, right now
1. What linkage IDs are worth having
At minimum, I'd generally recommend what you already have: Upstream ID and Downstream ID for your Line Features (i.e., Pipes). For probably 90% of use-cases, this is probably all you need. You might also consider an Outlet ID from your Polygon Features, like a detention pond. That's not handled at all by the code I provided below, though.
In our organization, we've also experimented with a suite of "helper" fields on our Point Feature, because it helps some of our older-school field people when they're locating new Assets. The goal was to not make them digitize a line feature from the field, so we needed a place to put the most critical datapoints for a newly-discovered pipe, like Material & Diameter. That evolved outward into back-populating the ID of the Pipe into one of those "helper" fields, once it's created. Downside is that it massively bloats your Point Feature's schema. It's a headache that I don't recommend doing lightly.
2. How to update linkage IDs for new features, as they're created:
Go with the code @RhettZufelt linked elsewhere in this thread, and build it as an Attribute Rule. It might or might not need a little retooling, depending on what linkage IDs you end up using. At a glance, it will work pretty much as-is, if all you have is Upstream ID / Downstream ID for a line feature.
(Sidebar: Thanks for finding that, Rhett—that step was in my near future and it's nice to not have to reinvent the wheel!)
3. How to update linkage IDs for existing features, right now:
A few assumptions/caveats/notes on this code, before I get to it:
This assumes your line features are snapped to your point features.
With the data we inherited in our organization, this wasn't originally the case.
You should be able to automate the snapping with existing ArcGIS tools, if memory serves, but it's been a while. Just be careful of the effect on Point 2 concerning duplicates, below.
You could also adapt the code to look for nodes within a certain radius, but that's quite a bit more memory-intensive, and we didn't need it to do so, so I also didn't write that in, here.
This assumes that you don't have any coincident duplicates in your point features.
Be careful if you have to do snapping from Point 1, because you might end up with duplicates afterwards, depending on your snap tolerances.
This tool could be modified relatively easily to handle duplicates if they exist, but I didn't write it this way, this time.
Lines 15-28 are your inputs, and they are not pre-populated.
If you want to run this standalone, just replace None on lines 19-20 with the full path to the relevant Feature Class. Then replace None on lines 23-28 with the name of the relevant field, as a string.
If you want to run this as a tool, replace None on all of the above lines with arcpy.GetParameterAsText(#), where # is the applicable index of the tool input for the tool you've created.
If you run this as a tool, it should automatically trigger progress bars. If you run it standalone, it'll have little/no reporting messages to provide you.
This is less-complex than what our organization uses, but slightly more-complex than what your OP demands.
Our code covers a bunch of other related circumstances and is currently in a bit of a rebuild, anyway, so I essentially rewrote a heavily-trimmed down stub.
I'm also pretty debug-happy with the stuff we actually run internally, because if/when something breaks, I like to know exactly where it broke and why, but I stripped all those steps out for the snippet below.
While it's a little more complex than what your OP necessitates, in my eyes it's a slightly better platform to build from if you do end up expanding or elaborating it in the future.
I can pretty much guarantee that this isn't the most-efficient possible way for this to run.
For one, I think the Spatial Join functions run more in C and/or in ArcGIS's back-end stuff, so I'm pretty sure they'd be more efficient. I didn't go that route with our code for a bunch of complicated reasons, though, and I decided not to rebuild it that way, here.
Our dataset is about 50k each for points and lines, at the moment, and it runs quickly enough on that set for our purposes. Plus, "There's nothing so permanent as a 'temporary' solution."
See the SPOILER below for the code, and happy automating!
"""Stormwater Upstream/Downstream Locator Tool
Author: MErikReedAugusta (ESRI Community)
Python: 3.9
ArcGIS Pro: 3.1.3
Scans the provided Storm Structure feature to generate a list of nodes, then matches those
nodes to the up/downstream ends of the provided Storm Pipe feature, and logs the relevant
Storm Structure ID.
"""
# IMPORTS Standard Library
# IMPORTS 3rd Party
import arcpy
##############################################################################################################################
# INPUTS
##############################################################################################################################
# The Feature Classes you intend to target with this tool
structure_Feature = None
pipe_Feature = None
# These are the ID Fields for each Asset ON THEIR RESPECTIVE FEATURE (This is their "self" ID)
structure_IDField = None
pipe_IDField = None
# These are the Upstream/Downstream ID Fields on the Pipe Feature
upstream_IDField = None
downstream_IDField = None
##############################################################################################################################
# CLASSES & EXCEPTIONS
##############################################################################################################################
class NodeNotFoundError(KeyError):
"""Exception raised when a specified Node couldn't be found in the NodeMap."""
pass
class DuplicateNodeError(KeyError):
"""Exception raised when a specified Node already exists in the NodeMap."""
pass
class NodeObj():
"""Defines a geographic point of significance for the geometry of an Asset in 2-dimensional space."""
def __init__(self, x, y):
self.X = x
self.Y = y
self.ID_Structure = None
class NodeMap():
"""A searchable list-like database of all Nodes encountered."""
def __init__(self):
self._data = []
#################################################################################################
# NodeMap Navigation
#################################################################################################
def HasNode(self, nodeX, nodeY):
"""Returns True if the provided X/Y Coordinate is found in the NodeMap."""
# Get a shortened list of just the nodes that match your coordinates.
matchingNodes = [node for node in self._data if node.X == nodeX and node.Y == nodeY]
# If the list is 0, then those coordinates aren't here. If it's bigger, then they are.
return len(matchingNodes) > 0
def GetNode(self, nodeX, nodeY):
"""Given a provided coordinate pair, this finds and returns the first matching NodeObj in the NodeMap."""
for node in self._data:
if node.X == nodeX and node.Y == nodeY:
return node
# If you're still here, then no nodes have matched. Probably should've checked first! Have an Exception, instead.
raise NodeNotFoundError()
def AddNode(self, nodeObj):
"""Adds the provided Node Object to the NodeMap."""
if self.HasNode(nodeObj.X, nodeObj.Y):
raise DuplicateNodeError()
self._data.append(nodeObj)
#################################################################################################
# Read/Write Assets
#################################################################################################
def ReadPoints(self):
"""Reads the Point Feature Class provided above and converts it into Node Objects."""
# Sets up an ArcGIS Progressor, if you're running this in a Tool
totalRows = int(arcpy.management.GetCount(structure_Feature)[0])
stepFrag = 10 ** (len(str(totalRows)) - 2)
arcpy.SetProgressor('step',
message='Populating Nodes from Structures...',
min_range=0,
max_range=totalRows,
step_value=stepFrag,
)
progCount = 0
# Iterates through the Structure Table, reading Nodes
with arcpy.da.SearchCursor(structure_Feature, ['OID@', structure_IDField, 'SHAPE@']) as cursor:
for oid, assetID, geo in cursor:
# Update the Progressor as we go, if this is in a Tool
progCount += 1
if progCount % stepFrag == 0:
arcpy.SetProgressorPosition()
arcpy.SetProgressorLabel(f'Reading Feature Class for Assets: {progCount: >{len(str(totalRows))}}/{totalRows}...')
# Do the actual work of pulling the Node
node = NodeObj(geo.firstPoint.X, geo.firstPoint.Y)
node.ID_Structure = assetID
self.AddNode(node)
# Clean up the node object(s) for safety & paranoia, so nothing gets forwarded where it shouldn't
del node
def WriteLines(self):
"""Reads the endpoints of the Line Feature Class provided above and retrieves the specified Node, if applicable."""
# Sets up an ArcGIS Progressor, if you're running this in a Tool
totalRows = int(arcpy.management.GetCount(pipe_Feature)[0])
stepFrag = 10 ** (len(str(totalRows)) - 2)
arcpy.SetProgressor('step',
message='Populating Nodes from Structures...',
min_range=0,
max_range=totalRows,
step_value=stepFrag,
)
progCount = 0
# Iterates through the Structure Table, reading Nodes
with arcpy.da.UpdateCursor(pipe_Feature, ['OID@', pipe_IDField, upstream_IDField, downstream_IDField, 'SHAPE@']) as cursor:
for oid, assetID, frID, toID, geo in cursor:
# Update the Progressor as we go, if this is in a Tool
progCount += 1
if progCount % stepFrag == 0:
arcpy.SetProgressorPosition()
arcpy.SetProgressorLabel(f'Reading Feature Class for Assets: {progCount: >{len(str(totalRows))}}/{totalRows}...')
# Look for the Upstream Node and read the ID there; write a sentinel value if it fails
try:
nodeObj_UP = self.GetNode(geo.firstPoint.X, geo.firstPoint.Y)
frID = nodeObj_UP.ID_Structure
except NodeNotFoundError():
frID = 'ID_Not_Found'
# Look for the Downstream Node and read the ID there; write a sentinel value if it fails
try:
nodeObj_DN = self.GetNode(geo.lastPoint.X, geo.lastPoint.Y)
toID = nodeObj_DN.ID_Structure
except NodeNotFoundError():
toID = 'ID_Not_Found'
cursor.updateRow((oid, assetID, frID, toID, geo))
# Clean up the node object(s) for safety & paranoia, so nothing gets forwarded where it shouldn't
del nodeObj_UP, nodeObj_DN
##############################################################################################################################
# MAIN
##############################################################################################################################
def Main():
arcpy.AddMessage('Begin.')
nodeMap = NodeMap()
arcpy.AddMessage('Populating Nodes from Structures...')
nodeMap.ReadPoints()
arcpy.AddMessage('Updating Linkage Fields in Pipes...')
nodeMap.WriteLines()
if __name__ == '__main__':
Main()
I have done this in the past with using a searchcursor to get the SHAPE@ field. With the SHAPE@ object you can get firstpoint/lastpoint of the line. ( line[1].lastPoint.X,line[1].lastPoint.Y )
I then iterate through each line and grab the first point (from the line) and use that point to make a feature layer so that I can use it to Select by Location the appropriate manhole ad get it's ID. Then I use an update cursor to assign this MH id to the UpstreamMH field.
Then, Select by Location using the last point, grab that MH id and update cursor to the DownstreamMH field.
I would think you could do this fairly easy with Arcade as well using the paths of the line, but have not looked into it. The main benefit of using Arcade is that you could also use it to make an attribute rule that will populate these values automagically in the future when editing/adding new features.
To add a little more, my data has separate featureclass for mahnoles or cleanouts on the sewer, and manholes, cleanouts, inlets, outlets, discharge, etc. on the storm.
So, to make the script easier so I only have to intersect with one layer, I run an append operation that copies over ALL the respective point features to a single temporary featureclass with the feature ID.
It looks like this post shows how to do that with Arcade. Is for an attribute rule, but shows the syntax that could be modified run in field calculator (or use as a rule 🙂
';
}
}
}
catch(e){
}
}
}
if (newSub.getAttribute("slang").toLowerCase() != code_l.toLowerCase()) {
if (trLabelsHtml != "") {
var labelSname = "";
if(labelEle[i].querySelector("ul li:nth-child(1)").getAttribute("aria-hidden")){
labelSname = labelEle[i].querySelector("ul li:nth-child(1)").outerHTML;
}
labelEle[i].innerHTML = "";
labelEle[i].innerHTML = labelSname + trLabelsHtml;
}
}
}
}
}
catch(e){
}
}
}
/* V 2.0:3 = Store not translated reply id */
if(lingoRSXML.snapshotLength == 0){
if($scope.falseReplyID == "") {
$scope.falseReplyID = value;
}
}
/* Get translated Body of Replies/Comments */
var lingoRBXML = doc.evaluate(lingoRBExp, doc, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for(var i=0;i 0) {
var attachDiv = rootElement.querySelector('div.lia-quilt-row-main').querySelector('div.custom-attachments');
if (attachDiv) {
attachDiv = attachDiv.outerHTML;
}
else if(rootElement.querySelector('div.lia-quilt-row-main').querySelectorAll('#attachments').length > 0){
if ("ForumTopicPage" == "BlogArticlePage") {
attachDiv = rootElement.querySelector('div.lia-quilt-row-main .lia-message-body-content').querySelector('#attachments');
if (attachDiv) {
attachDiv = attachDiv.outerHTML;
}
else{
attachDiv = "";
}
}else{
attachDiv = rootElement.querySelector('div.lia-quilt-row-main').querySelector('#attachments').outerHTML;
}
}
else {
attachDiv = "";
}
/* Feedback Div */
var feedbackDiv = "";
var feedbackDivs = rootElement.querySelector('div.lia-quilt-row-main').querySelectorAll('div.lia-panel-feedback-banner-safe');
if (feedbackDivs.length > 0) {
for (var k = 0; k < feedbackDivs.length; k++) {
feedbackDiv = feedbackDiv + feedbackDivs[k].outerHTML;
}
}
}
else {
var attachDiv = rootElement.querySelector('div.lia-message-body-content').querySelector('div.Attachments.preview-attachments');
if (attachDiv) {
attachDiv = attachDiv.outerHTML;
} else {
attachDiv = "";
}
/* Everyone tags links */
if (document.querySelectorAll("div.TagList").length > 0){
var everyoneTagslink = document.querySelector('div.lia-quilt-row-main').querySelector(".MessageTagsTaplet .TagList");
if ((everyoneTagslink != null)||(everyoneTagslink != undefined)){
everyoneTagslink = everyoneTagslink.outerHTML;
}
else{
everyoneTagslink = "";
}
}
/* Feedback Div */
var feedbackDiv = "";
var feedbackDivs = rootElement.querySelector('div.lia-message-body-content').querySelectorAll('div.lia-panel-feedback-banner-safe');
if (feedbackDivs.length > 0) {
for (var m = 0; m < feedbackDivs.length; m++) {
feedbackDiv = feedbackDiv + feedbackDivs[m].outerHTML;
}
}
}
}
} catch (e) {
}
if (body_L == "") {
/* V 2.0:7 Replacing translated video data with source video data */
var newBodyVideoData = newBody.querySelectorAll('div[class*="video-embed"]');
angular.forEach($scope.videoData[value], function (sourceVideoElement, index) {
if (index <= (newBodyVideoData.length - 1)) {
newBodyVideoData[index].outerHTML = sourceVideoElement.outerHTML
}
});
/* V 2.0:7 = Replacing translated image data with source data */
var newBodyImageData = newBody.querySelectorAll('[class*="lia-image"]');
angular.forEach($scope.imageData[value], function (sourceImgElement, index) {
if (index <= (newBodyImageData.length - 1)) {
newBodyImageData[index].outerHTML = sourceImgElement.outerHTML;
}
});
/* V 2.0:7 = Replacing translated pre tag data with source data */
var newBodyPreTagData = newBody.querySelectorAll('pre');
angular.forEach($scope.preTagData[value], function (sourcePreTagElement, index) {
if (index <= (newBodyPreTagData.length - 1)) {
newBodyPreTagData[index].outerHTML = sourcePreTagElement.outerHTML;
}
});
}
var copyBodySubject = false;
if (body_L == "") {
copyBodySubject = true;
body_L = newBody.innerHTML;
}
/* This code is written as part of video fix by iTalent */
/* try{
var iframeHTMLText = body_L;
var searchIframeText = "<IFRAME";
var foundiFrameTag;
if (iframeHTMLText.indexOf(searchIframeText) > -1) {
foundiFrameTag = decodeHTMLEntities(iframeHTMLText);
foundiFrameTag = foundiFrameTag.split('src="')[1];
body_L = foundiFrameTag;
}
}
catch(e){
} */
/* This code is placed to remove the extra meta tag adding in the UI*/
try{
body_L = body_L.replace('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />','');
}
catch(e){
}
/** We should not replace the source content if user profile language and selected target language matches with source language **/
if(showTrContent) {
var compiled = false;
rootElement.querySelectorAll('div.lia-message-body-content')[0].innerHTML = null
if("ForumTopicPage"=="IdeaPage"){
// var customAttachDiv = '';
rootElement.querySelectorAll('div.lia-message-body-content')[0].innerHTML = body_L + feedbackDiv ;
$compile(rootElement.querySelectorAll('div.lia-message-body-content')[0])($scope);
compiled = true;
/* Attach atttach div */
// document.querySelector("div.translation-attachments-"+value).innerHTML = attachDiv;
rootElement.querySelectorAll('div.lia-message-body-content')[0].insertAdjacentHTML('afterend',attachDiv);
if(rootElement.querySelectorAll('div.lia-quilt-idea-message .lia-message-body .lia-attachments-message').length > 1){
rootElement.querySelectorAll('div.lia-quilt-idea-message .lia-message-body .lia-attachments-message')[1].remove();
}
} else {
if("ForumTopicPage"=="TkbArticlePage"){
rootElement.querySelectorAll('div.lia-message-body-content')[0].innerHTML = body_L + feedbackDiv ;
}else{
rootElement.querySelectorAll('div.lia-message-body-content')[0].innerHTML = body_L + feedbackDiv + attachDiv;
compiled = true;
}
}
/* Destroy and recreate OOyala player videos to restore the videos in target languages which is written by iTalent as part of iTrack LILICON-79 */ /* Destroy and recreate OOyala player videos */
try{
// $scope.videoData[value][0].querySelector("div").getAttribute("id");
for(var vidIndex=0; vidIndex<$scope.videoData[value].length; vidIndex++){
if( $scope.videoData[value][vidIndex].querySelector("div") != null){
var containerId = LITHIUM.OOYALA.players[$scope.videoData[value][vidIndex].querySelector("div").getAttribute("id")].containerId;
videoId = LITHIUM.OOYALA.players[$scope.videoData[value][vidIndex].querySelector("div").getAttribute("id")].videoId;
/** Get the Video object */
vid = OO.Player.create(containerId,videoId);
/** Destroy the video **/
vid.destroy();
/** recreate in the same position */
var vid = OO.Player.create(containerId,videoId);
}
}
}
catch(e){
}
try{
for(var vidIndex=0; vidIndex<($scope.videoData[value].length); vidIndex++){
if($scope.videoData[value][vidIndex].querySelector('video-js') != null){
var data_id = $scope.videoData[value][vidIndex].querySelector('video-js').getAttribute('data-video-id');
var data_account = $scope.videoData[value][vidIndex].querySelector('video-js').getAttribute('data-account');
var data_palyer = $scope.videoData[value][vidIndex].querySelector('video-js').getAttribute('data-player');
var div = document.createElement('div');
div.id = "brightcove";
div.class = "brightcove-player";
div.innerHTML =
'(view in my videos)'
var data = div.getElementsByClassName("video-js");
var script = document.createElement('script');
script.src = "https://players.brightcove.net/" + data_account + "/" + data_palyer + "_default/index.min.js";
for(var i=0;i< data.length;i++){
videodata.push(data[i]);
}
}
}
for(var i=0;i< videodata.length;i++){
document.getElementsByClassName('lia-vid-container')[i].innerHTML = videodata[i].outerHTML;
document.body.appendChild(script);
}
}
catch(e){
}
if(!compiled){
/* Re compile html */
$compile(rootElement.querySelectorAll('div.lia-message-body-content')[0])($scope);
}
}
if (code_l.toLowerCase() != newBody.getAttribute("slang").toLowerCase()) {
/* Adding Translation flag */
var tr_obj = $filter('filter')($scope.sourceLangList, function (obj_l) {
return obj_l.code.toLowerCase() === newBody.getAttribute("slang").toLowerCase()
});
if (tr_obj.length > 0) {
tr_text = "Esri may utilize third parties to translate your data and/or imagery to facilitate communication across different languages.".replace(/lilicon-trans-text/g, tr_obj[0].title);
try {
if ($scope.wootMessages[$rootScope.profLang] != undefined) {
tr_text = $scope.wootMessages[$rootScope.profLang].replace(/lilicon-trans-text/g, tr_obj[0].title);
}
} catch (e) {
}
} else {
//tr_text = "This message was translated for your convenience!";
tr_text = "Esri may utilize third parties to translate your data and/or imagery to facilitate communication across different languages.";
}
try {
if (!document.getElementById("tr-msz-" + value)) {
var tr_para = document.createElement("P");
tr_para.setAttribute("id", "tr-msz-" + value);
tr_para.setAttribute("class", "tr-msz");
tr_para.style.textAlign = 'justify';
var tr_fTag = document.createElement("IMG");
tr_fTag.setAttribute("class", "tFlag");
tr_fTag.setAttribute("src", "/html/assets/langTrFlag.PNG");
tr_fTag.style.marginRight = "5px";
tr_fTag.style.height = "14px";
tr_para.appendChild(tr_fTag);
var tr_textNode = document.createTextNode(tr_text);
tr_para.appendChild(tr_textNode);
/* Woot message only for multi source */
if(rootElement.querySelector(".lia-quilt-forum-message")){
rootElement.querySelector(".lia-quilt-forum-message").appendChild(tr_para);
} else if(rootElement.querySelector(".lia-message-view-blog-topic-message")) {
rootElement.querySelector(".lia-message-view-blog-topic-message").appendChild(tr_para);
} else if(rootElement.querySelector(".lia-quilt-blog-reply-message")){
rootElement.querySelector(".lia-quilt-blog-reply-message").appendChild(tr_para);
} else if(rootElement.querySelector(".lia-quilt-tkb-message")){
rootElement.querySelector(".lia-quilt-tkb-message").appendChild(tr_para);
} else if(rootElement.querySelector(".lia-quilt-tkb-reply-message")){
rootElement.querySelector(".lia-quilt-tkb-reply-message").insertBefore(tr_para,rootElement.querySelector(".lia-quilt-row.lia-quilt-row-footer"));
} else if(rootElement.querySelector(".lia-quilt-idea-message")){
rootElement.querySelector(".lia-quilt-idea-message").appendChild(tr_para);
} else if(rootElement.querySelector('.lia-quilt-occasion-message')){
rootElement.querySelector('.lia-quilt-occasion-message').appendChild(tr_para);
}
else {
if (rootElement.querySelectorAll('div.lia-quilt-row-footer').length > 0) {
rootElement.querySelectorAll('div.lia-quilt-row-footer')[0].appendChild(tr_para);
} else {
rootElement.querySelectorAll('div.lia-quilt-column-message-footer')[0].appendChild(tr_para);
}
}
}
} catch (e) {
}
}
} else {
/* Do not display button for same language */
// syncList.remove(value);
var index = $scope.syncList.indexOf(value);
if (index > -1) {
$scope.syncList.splice(index, 1);
}
}
}
}
});
});
/* V 1.1:2 = Reply Sync button for multi source translation */
} catch(e){
console.log(e);
}
};
if((rContent != undefined) && (rContent != "")) {
drawCanvas(decodeURIComponent(rContent));
/** Update variable with selected language code **/
$scope.previousSelCode = code_l;
}
};
/**
* @function manageTranslation
* @description Managess the translation of given language for the thread
* @param {string} langCode - Language Code
* @param {string} tid - Thread ID
*/
$scope.manageTranslation = function (langCode, tid) {
//debugger;
$scope.showTrText = false;
/* V 2.0:5 = actualStatus variable introduced to indicate detailed connector status on UI. This variable holds the actual translation percentage */
$scope.transPercent = "";
$scope.actualStatus = "";
if (tid != "") {
var bulkTranslation = lithiumPlugin.bulkTranslation(langCode, tid);
bulkTranslation.then(function (trContent) {
if(trContent.body != "") {
$scope.showPreview(trContent.body, $scope.mszList, langCode);
if(langCode != "en-US") {
$scope.showTrText = true;
}
}
if((trContent.status != "NA") && trContent.status != null) {
// $scope.transPercent = String(trContent.status);
$scope.actualStatus = String(trContent.status);
} else {
// $rootScope.errorMsg = "Translation is in progress. Please check again a few minutes."
$rootScope.errorMsg = "Translation is in progress. Please retry in a few minutes."
}
$scope.workbench = trContent.wb;
/* V 2.0:4 = Trigger uncalled or delayed callbacks (documnet uploaded/translation completed from lithium).*/
if(trContent.callback == 'true') {
var trCompletCallback = lithiumPlugin.trCompletCallback(langCode, trContent.docID);
trCompletCallback.then(function (callback){
// $rootScope.errorMsg = "Downloading Translated content in " + langCode + " now. Please check again in a few minutes."
$rootScope.errorMsg = "Uploading content to translate. Please check again in a few minutes."
});
} else if (trContent.callback == 'upload') {
var trCompletUpload = lithiumPlugin.trCompletUpload(langCode, trContent.docID);
trCompletUpload.then(function (callback) {
//$rootScope.errorMsg = "Uploading content to translate. Please check again in a few minutes."
$rootScope.errorMsg = "Uploading content to translate. Please check again in a few minutes."
});
} else if ("many" == "one") {
$scope.updateOOS();
} else if("SmartConx" == "SmartConx"){
if ("many" == "many"){
$scope.updateOOS();
}
}else if ((trContent.status != null) && trContent.status.includes("100")) {
/* If everything fine then only check Out of Sync status */
$scope.updateOOS();
} else {
/* If translation perccent is less than 100 then show the percentage on UI */
$scope.transPercent = $scope.actualStatus;
}
});
}
}
/**
* @function selectThisLang
* @description Called on select dropdown.
* @param {string} lang - Language code
*
*/
$scope.selectThisLang = function (lang, anonymousFlag) {
/* 1.4:3 Update Analytics on language selection */
try {
lingoThreadLangSelected(lang, '1387457');
} catch (e) {
}
/** Display Translated content **/
var getTranslation = lithiumPlugin.getTranslation(lang, "1387457");
getTranslation.then(function (trContent) {
if (trContent.body != "") {
$scope.showPreview(trContent.body, $scope.mszList, lang);
} else {
//$rootScope.errorMsg = "Translation is in progress. Please check again in a few minutes."
$rootScope.errorMsg = "Translation is in progress. Please retry in a few minutes."
}
});
};
var decodeEntities = (function() {
// this prevents any overhead from creating the object each time
var element = document.createElement('div');
function decodeHTMLEntities (str) {
if(str && typeof str === 'string') {
// strip script/html tags
str = str.replace(/