Browse Source

Merge branch 'master' into develop/2.3

pull/1463/head
Igor Kulikov 8 years ago
parent
commit
cd1d60c9e5
  1. 91
      dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java
  2. 2
      dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java
  3. 34
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java
  4. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java
  5. 97
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java
  6. 6
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java
  7. 45
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java
  8. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java
  9. 8
      rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js
  10. 1
      ui/src/app/locale/locale.constant-de_DE.json
  11. 1
      ui/src/app/locale/locale.constant-en_US.json
  12. 3
      ui/src/app/locale/locale.constant-es_ES.json
  13. 1
      ui/src/app/locale/locale.constant-fr_FR.json
  14. 1
      ui/src/app/locale/locale.constant-it_IT.json
  15. 1
      ui/src/app/locale/locale.constant-ja_JA.json
  16. 1
      ui/src/app/locale/locale.constant-ko_KR.json
  17. 1
      ui/src/app/locale/locale.constant-ru_RU.json
  18. 3
      ui/src/app/locale/locale.constant-tr_TR.json
  19. 1
      ui/src/app/locale/locale.constant-zh_CN.json
  20. 28
      ui/src/app/widget/lib/google-map.js
  21. 105
      ui/src/app/widget/lib/map-widget2.js
  22. 12
      ui/src/app/widget/lib/openstreet-map.js
  23. 25
      ui/src/app/widget/lib/tencent-map.js
  24. 34
      ui/src/app/widget/lib/widget-utils.js

91
dao/src/main/java/org/thingsboard/server/dao/model/sql/TsKvEntity.java

@ -62,49 +62,53 @@ public final class TsKvEntity implements ToData<TsKvEntry> {
} }
public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) { public TsKvEntity(Long longValue, Double doubleValue, Long longCountValue, Long doubleCountValue, String aggType) {
switch (aggType) { if(!isAllNull(longValue, doubleValue, longCountValue, doubleCountValue)) {
case AVG: switch (aggType) {
double sum = 0.0; case AVG:
if (longValue != null) { double sum = 0.0;
sum += longValue; if (longValue != null) {
} sum += longValue;
if (doubleValue != null) { }
sum += doubleValue; if (doubleValue != null) {
} sum += doubleValue;
long totalCount = longCountValue + doubleCountValue; }
if (totalCount > 0) { long totalCount = longCountValue + doubleCountValue;
this.doubleValue = sum / (longCountValue + doubleCountValue); if (totalCount > 0) {
} else { this.doubleValue = sum / (longCountValue + doubleCountValue);
this.doubleValue = 0.0; } else {
} this.doubleValue = 0.0;
break; }
case SUM: break;
if (doubleCountValue > 0) { case SUM:
this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0); if (doubleCountValue > 0) {
} else { this.doubleValue = doubleValue + (longValue != null ? longValue.doubleValue() : 0.0);
this.longValue = longValue; } else {
} this.longValue = longValue;
break; }
case MIN: break;
case MAX: case MIN:
if (longCountValue > 0 && doubleCountValue > 0) { case MAX:
this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue()); if (longCountValue > 0 && doubleCountValue > 0) {
} else if (doubleCountValue > 0) { this.doubleValue = MAX.equals(aggType) ? Math.max(doubleValue, longValue.doubleValue()) : Math.min(doubleValue, longValue.doubleValue());
this.doubleValue = doubleValue; } else if (doubleCountValue > 0) {
} else if (longCountValue > 0) { this.doubleValue = doubleValue;
this.longValue = longValue; } else if (longCountValue > 0) {
} this.longValue = longValue;
break; }
break;
}
} }
} }
public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) { public TsKvEntity(Long booleanValueCount, Long strValueCount, Long longValueCount, Long doubleValueCount) {
if (booleanValueCount != 0) { if(!isAllNull(booleanValueCount, strValueCount, longValueCount, doubleValueCount)) {
this.longValue = booleanValueCount; if (booleanValueCount != 0) {
} else if (strValueCount != 0) { this.longValue = booleanValueCount;
this.longValue = strValueCount; } else if (strValueCount != 0) {
} else { this.longValue = strValueCount;
this.longValue = longValueCount + doubleValueCount; } else {
this.longValue = longValueCount + doubleValueCount;
}
} }
} }
@ -155,4 +159,13 @@ public final class TsKvEntity implements ToData<TsKvEntry> {
public boolean isNotEmpty() { public boolean isNotEmpty() {
return strValue != null || longValue != null || doubleValue != null || booleanValue != null; return strValue != null || longValue != null || doubleValue != null || booleanValue != null;
} }
private static boolean isAllNull(Object... args) {
for (Object arg : args) {
if(arg != null) {
return false;
}
}
return true;
}
} }

2
dao/src/main/java/org/thingsboard/server/dao/timeseries/BaseTimeseriesService.java

@ -148,7 +148,7 @@ public class BaseTimeseriesService implements TimeseriesService {
private void saveAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) { private void saveAndRegisterFutures(TenantId tenantId, List<ListenableFuture<Void>> futures, EntityId entityId, TsKvEntry tsKvEntry, long ttl) {
if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) { if (entityId.getEntityType().equals(EntityType.ENTITY_VIEW)) {
throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Only read only"); throw new IncorrectParameterException("Telemetry data can't be stored for entity view. Read only");
} }
futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl)); futures.add(timeseriesDao.savePartition(tenantId, entityId, tsKvEntry.getTs(), tsKvEntry.getKey(), ttl));
futures.add(timeseriesDao.saveLatest(tenantId, entityId, tsKvEntry)); futures.add(timeseriesDao.saveLatest(tenantId, entityId, tsKvEntry));

34
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java

@ -22,6 +22,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.thingsboard.rule.engine.api.TbContext; import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNode; import org.thingsboard.rule.engine.api.TbNode;
@ -78,28 +79,28 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
@Override @Override
public void onMsg(TbContext ctx, TbMsg msg) { public void onMsg(TbContext ctx, TbMsg msg) {
withCallback(processEntityRelationAction(ctx, msg), withCallback(processEntityRelationAction(ctx, msg),
filterResult -> ctx.tellNext(msg, filterResult ? SUCCESS : FAILURE), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor()); filterResult -> ctx.tellNext(filterResult.getMsg(), filterResult.isResult() ? SUCCESS : FAILURE), t -> ctx.tellFailure(msg, t), ctx.getDbCallbackExecutor());
} }
@Override @Override
public void destroy() { public void destroy() {
} }
protected ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) { protected ListenableFuture<RelationContainer> processEntityRelationAction(TbContext ctx, TbMsg msg) {
return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer)); return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer));
} }
protected abstract boolean createEntityIfNotExists(); protected abstract boolean createEntityIfNotExists();
protected abstract ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer); protected abstract ListenableFuture<RelationContainer> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer);
protected abstract C loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException; protected abstract C loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException;
protected ListenableFuture<EntityContainer> getEntity(TbContext ctx, TbMsg msg) { protected ListenableFuture<EntityContainer> getEntity(TbContext ctx, TbMsg msg) {
String entityName = TbNodeUtils.processPattern(this.config.getEntityNamePattern(), msg.getMetaData()); String entityName = processPattern(msg, this.config.getEntityNamePattern());
String type; String type;
if (this.config.getEntityTypePattern() != null) { if (this.config.getEntityTypePattern() != null) {
type = TbNodeUtils.processPattern(this.config.getEntityTypePattern(), msg.getMetaData()); type = processPattern(msg, this.config.getEntityTypePattern());
} else { } else {
type = null; type = null;
} }
@ -116,24 +117,30 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
protected SearchDirectionIds processSingleSearchDirection(TbMsg msg, EntityContainer entityContainer) { protected SearchDirectionIds processSingleSearchDirection(TbMsg msg, EntityContainer entityContainer) {
SearchDirectionIds searchDirectionIds = new SearchDirectionIds(); SearchDirectionIds searchDirectionIds = new SearchDirectionIds();
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { if (EntitySearchDirection.FROM.name().equals(this.config.getDirection())) {
searchDirectionIds.setFromId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString())); searchDirectionIds.setFromId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()));
searchDirectionIds.setToId(msg.getOriginator()); searchDirectionIds.setToId(msg.getOriginator());
searchDirectionIds.setOrignatorDirectionFrom(false);
} else { } else {
searchDirectionIds.setToId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString())); searchDirectionIds.setToId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()));
searchDirectionIds.setFromId(msg.getOriginator()); searchDirectionIds.setFromId(msg.getOriginator());
searchDirectionIds.setOrignatorDirectionFrom(true);
} }
return searchDirectionIds; return searchDirectionIds;
} }
protected ListenableFuture<List<EntityRelation>> processListSearchDirection(TbContext ctx, TbMsg msg) { protected ListenableFuture<List<EntityRelation>> processListSearchDirection(TbContext ctx, TbMsg msg) {
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { if (EntitySearchDirection.FROM.name().equals(this.config.getDirection())) {
return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON); return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationTypePattern()), RelationTypeGroup.COMMON);
} else { } else {
return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), config.getRelationType(), RelationTypeGroup.COMMON); return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), processPattern(msg, this.config.getRelationTypePattern()), RelationTypeGroup.COMMON);
} }
} }
protected String processPattern(TbMsg msg, String pattern){
return TbNodeUtils.processPattern(pattern, msg.getMetaData());
}
@Data @Data
@AllArgsConstructor @AllArgsConstructor
private static class EntityKey { private static class EntityKey {
@ -146,6 +153,7 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
protected static class SearchDirectionIds { protected static class SearchDirectionIds {
private EntityId fromId; private EntityId fromId;
private EntityId toId; private EntityId toId;
private boolean orignatorDirectionFrom;
} }
private static class EntityCacheLoader extends CacheLoader<EntityKey, EntityContainer> { private static class EntityCacheLoader extends CacheLoader<EntityKey, EntityContainer> {
@ -233,7 +241,15 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
} }
return targetEntity; return targetEntity;
} }
}
@Data
@NoArgsConstructor
@AllArgsConstructor
protected static class RelationContainer {
private TbMsg msg;
private boolean result;
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNodeConfiguration.java

@ -21,7 +21,7 @@ import lombok.Data;
public abstract class TbAbstractRelationActionNodeConfiguration { public abstract class TbAbstractRelationActionNodeConfiguration {
private String direction; private String direction;
private String relationType; private String relationTypePattern;
private String entityType; private String entityType;
private String entityNamePattern; private String entityNamePattern;

97
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java

@ -35,13 +35,20 @@ import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.msg.TbMsg; import org.thingsboard.server.common.msg.TbMsg;
import java.util.ArrayList;
import java.util.List;
@Slf4j @Slf4j
@RuleNode( @RuleNode(
type = ComponentType.ACTION, type = ComponentType.ACTION,
name = "create relation", name = "create relation",
configClazz = TbCreateRelationNodeConfiguration.class, configClazz = TbCreateRelationNodeConfiguration.class,
nodeDescription = "Finds target Entity by entity name pattern and (entity type pattern for Asset, Device) and then create a relation to Originator Entity by type and direction." + nodeDescription = "Finds target Entity by entity name pattern and (entity type pattern for Asset, Device) and then create a relation to Originator Entity by type and direction." +
" If Selected entity type: Asset, Device or Customer will create new Entity if it doesn't exist and 'Create new entity if not exists' is set to true.", " If Selected entity type: Asset, Device or Customer will create new Entity if it doesn't exist and selected checkbox 'Create new entity if not exists'.<br>" +
" In case that relation from the message originator to the selected entity not exist and If selected checkbox 'Remove current relations'," +
" before creating the new relation all existed relations to message originator by type and direction will be removed.<br>" +
" If relation from the message originator to the selected entity created and If selected checkbox 'Change originator to related entity'," +
" outbound message will be processed as a message from this entity.",
nodeDetails = "If the relation already exists or successfully created - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.", nodeDetails = "If the relation already exists or successfully created - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective = "tbActionNodeCreateRelationConfig", configDirective = "tbActionNodeCreateRelationConfig",
@ -49,6 +56,8 @@ import org.thingsboard.server.common.msg.TbMsg;
) )
public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateRelationNodeConfiguration> { public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateRelationNodeConfiguration> {
private String relationType;
@Override @Override
protected TbCreateRelationNodeConfiguration loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException { protected TbCreateRelationNodeConfiguration loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException {
return TbNodeUtils.convert(configuration, TbCreateRelationNodeConfiguration.class); return TbNodeUtils.convert(configuration, TbCreateRelationNodeConfiguration.class);
@ -60,19 +69,60 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
} }
@Override @Override
protected ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entity) { protected ListenableFuture<RelationContainer> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entity) {
return createIfAbsent(ctx, msg, entity); ListenableFuture<Boolean> future = createIfAbsent(ctx, msg, entity);
return Futures.transform(future, result -> {
RelationContainer container = new RelationContainer();
if (result && config.isChangeOriginatorToRelatedEntity()) {
TbMsg tbMsg = ctx.transformMsg(msg, msg.getType(), entity.getEntityId(), msg.getMetaData(), msg.getData());
container.setMsg(tbMsg);
} else {
container.setMsg(msg);
}
container.setResult(result);
return container;
});
} }
private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
relationType = processPattern(msg, config.getRelationTypePattern());
SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer); SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON), ListenableFuture<Boolean> checkRelationFuture = Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), relationType, RelationTypeGroup.COMMON), result -> {
result -> { if (!result) {
if (!result) { if (config.isRemoveCurrentRelations()) {
return processCreateRelation(ctx, entityContainer, sdId); return processDeleteRelations(ctx, processFindRelations(ctx, msg, sdId));
} }
return Futures.immediateFuture(true); }
}); return Futures.immediateFuture(true);
}, ctx.getDbCallbackExecutor());
return Futures.transformAsync(checkRelationFuture, result -> {
if (!result) {
return processCreateRelation(ctx, entityContainer, sdId);
}
return Futures.immediateFuture(true);
}, ctx.getDbCallbackExecutor());
}
private ListenableFuture<List<EntityRelation>> processFindRelations(TbContext ctx, TbMsg msg, SearchDirectionIds sdId) {
if (sdId.isOrignatorDirectionFrom()) {
return ctx.getRelationService().findByFromAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), relationType, RelationTypeGroup.COMMON);
} else {
return ctx.getRelationService().findByToAndTypeAsync(ctx.getTenantId(), msg.getOriginator(), relationType, RelationTypeGroup.COMMON);
}
}
private ListenableFuture<Boolean> processDeleteRelations(TbContext ctx, ListenableFuture<List<EntityRelation>> listListenableFuture) {
return Futures.transformAsync(listListenableFuture, entityRelations -> {
if (!entityRelations.isEmpty()) {
List<ListenableFuture<Boolean>> list = new ArrayList<>();
for (EntityRelation relation : entityRelations) {
list.add(ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), relation));
}
return Futures.transform(Futures.allAsList(list), result -> false);
}
return Futures.immediateFuture(false);
}, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processCreateRelation(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processCreateRelation(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
@ -96,17 +146,17 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
private ListenableFuture<Boolean> processView(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processView(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), new EntityViewId(entityContainer.getEntityId().getId())), entityView -> { return Futures.transformAsync(ctx.getEntityViewService().findEntityViewByIdAsync(ctx.getTenantId(), new EntityViewId(entityContainer.getEntityId().getId())), entityView -> {
if (entityView != null) { if (entityView != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); }, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processDevice(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processDevice(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getDeviceService().findDeviceByIdAsync(ctx.getTenantId(), new DeviceId(entityContainer.getEntityId().getId())), device -> { return Futures.transformAsync(ctx.getDeviceService().findDeviceByIdAsync(ctx.getTenantId(), new DeviceId(entityContainer.getEntityId().getId())), device -> {
if (device != null) { if (device != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
@ -116,40 +166,45 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
private ListenableFuture<Boolean> processAsset(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processAsset(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), new AssetId(entityContainer.getEntityId().getId())), asset -> { return Futures.transformAsync(ctx.getAssetService().findAssetByIdAsync(ctx.getTenantId(), new AssetId(entityContainer.getEntityId().getId())), asset -> {
if (asset != null) { if (asset != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); }, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processCustomer(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processCustomer(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), new CustomerId(entityContainer.getEntityId().getId())), customer -> { return Futures.transformAsync(ctx.getCustomerService().findCustomerByIdAsync(ctx.getTenantId(), new CustomerId(entityContainer.getEntityId().getId())), customer -> {
if (customer != null) { if (customer != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); }, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processDashboard(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processDashboard(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getDashboardService().findDashboardByIdAsync(ctx.getTenantId(), new DashboardId(entityContainer.getEntityId().getId())), dashboard -> { return Futures.transformAsync(ctx.getDashboardService().findDashboardByIdAsync(ctx.getTenantId(), new DashboardId(entityContainer.getEntityId().getId())), dashboard -> {
if (dashboard != null) { if (dashboard != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); }, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processTenant(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processTenant(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
return Futures.transformAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), new TenantId(entityContainer.getEntityId().getId())), tenant -> { return Futures.transformAsync(ctx.getTenantService().findTenantByIdAsync(ctx.getTenantId(), new TenantId(entityContainer.getEntityId().getId())), tenant -> {
if (tenant != null) { if (tenant != null) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON)); return processSave(ctx, sdId);
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); }, ctx.getDbCallbackExecutor());
} }
private ListenableFuture<Boolean> processSave(TbContext ctx, SearchDirectionIds sdId) {
return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), relationType, RelationTypeGroup.COMMON));
}
} }

6
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNodeConfiguration.java

@ -23,15 +23,19 @@ import org.thingsboard.server.common.data.relation.EntitySearchDirection;
public class TbCreateRelationNodeConfiguration extends TbAbstractRelationActionNodeConfiguration implements NodeConfiguration<TbCreateRelationNodeConfiguration> { public class TbCreateRelationNodeConfiguration extends TbAbstractRelationActionNodeConfiguration implements NodeConfiguration<TbCreateRelationNodeConfiguration> {
private boolean createEntityIfNotExists; private boolean createEntityIfNotExists;
private boolean changeOriginatorToRelatedEntity;
private boolean removeCurrentRelations;
@Override @Override
public TbCreateRelationNodeConfiguration defaultConfiguration() { public TbCreateRelationNodeConfiguration defaultConfiguration() {
TbCreateRelationNodeConfiguration configuration = new TbCreateRelationNodeConfiguration(); TbCreateRelationNodeConfiguration configuration = new TbCreateRelationNodeConfiguration();
configuration.setDirection(EntitySearchDirection.FROM.name()); configuration.setDirection(EntitySearchDirection.FROM.name());
configuration.setRelationType("Contains"); configuration.setRelationTypePattern("Contains");
configuration.setEntityNamePattern(""); configuration.setEntityNamePattern("");
configuration.setEntityCacheExpiration(300); configuration.setEntityCacheExpiration(300);
configuration.setCreateEntityIfNotExists(false); configuration.setCreateEntityIfNotExists(false);
configuration.setRemoveCurrentRelations(false);
configuration.setChangeOriginatorToRelatedEntity(false);
return configuration; return configuration;
} }
} }

45
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java

@ -42,11 +42,13 @@ import java.util.List;
" if 'Delete single entity' is set to true, otherwise rule node will delete all relations to the originator of the message by type and direction.", " if 'Delete single entity' is set to true, otherwise rule node will delete all relations to the originator of the message by type and direction.",
nodeDetails = "If the relation(s) successfully deleted - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.", nodeDetails = "If the relation(s) successfully deleted - Message send via <b>Success</b> chain, otherwise <b>Failure</b> chain will be used.",
uiResources = {"static/rulenode/rulenode-core-config.js"}, uiResources = {"static/rulenode/rulenode-core-config.js"},
configDirective ="tbActionNodeDeleteRelationConfig", configDirective = "tbActionNodeDeleteRelationConfig",
icon = "remove_circle" icon = "remove_circle"
) )
public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteRelationNodeConfiguration> { public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteRelationNodeConfiguration> {
private String relationType;
@Override @Override
protected TbDeleteRelationNodeConfiguration loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException { protected TbDeleteRelationNodeConfiguration loadEntityNodeActionConfig(TbNodeConfiguration configuration) throws TbNodeException {
return TbNodeUtils.convert(configuration, TbDeleteRelationNodeConfiguration.class); return TbNodeUtils.convert(configuration, TbDeleteRelationNodeConfiguration.class);
@ -58,35 +60,39 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteR
} }
@Override @Override
protected ListenableFuture<Boolean> processEntityRelationAction(TbContext ctx, TbMsg msg) { protected ListenableFuture<RelationContainer> processEntityRelationAction(TbContext ctx, TbMsg msg) {
if(config.isDeleteForSingleEntity()){ return getRelationContainerListenableFuture(ctx, msg);
return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer));
} else {
return processList(ctx, msg);
}
} }
@Override @Override
protected ListenableFuture<Boolean> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { protected ListenableFuture<RelationContainer> doProcessEntityRelationAction(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
return processSingle(ctx, msg, entityContainer); return Futures.transform(processSingle(ctx, msg, entityContainer), result -> new RelationContainer(msg, result));
} }
private ListenableFuture<RelationContainer> getRelationContainerListenableFuture(TbContext ctx, TbMsg msg) {
relationType = processPattern(msg, config.getRelationTypePattern());
if (config.isDeleteForSingleEntity()) {
return Futures.transformAsync(getEntity(ctx, msg), entityContainer -> doProcessEntityRelationAction(ctx, msg, entityContainer));
} else {
return Futures.transform(processList(ctx, msg), result -> new RelationContainer(msg, result));
}
}
private ListenableFuture<Boolean> processList(TbContext ctx, TbMsg msg) { private ListenableFuture<Boolean> processList(TbContext ctx, TbMsg msg) {
return Futures.transformAsync(processListSearchDirection(ctx, msg), entityRelations -> { return Futures.transformAsync(processListSearchDirection(ctx, msg), entityRelations -> {
if(entityRelations.isEmpty()){ if (entityRelations.isEmpty()) {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} else { } else {
List<ListenableFuture<Boolean>> listenableFutureList = new ArrayList<>(); List<ListenableFuture<Boolean>> listenableFutureList = new ArrayList<>();
for (EntityRelation entityRelation: entityRelations) { for (EntityRelation entityRelation : entityRelations) {
listenableFutureList.add(ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), entityRelation)); listenableFutureList.add(ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), entityRelation));
} }
return Futures.transformAsync(Futures.allAsList(listenableFutureList), booleans -> { return Futures.transformAsync(Futures.allAsList(listenableFutureList), booleans -> {
for (Boolean bool : booleans) { for (Boolean bool : booleans) {
if (!bool) { if (!bool) {
return Futures.immediateFuture(false); return Futures.immediateFuture(false);
} }
} }
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
}); });
} }
}); });
@ -94,8 +100,7 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteR
private ListenableFuture<Boolean> processSingle(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { private ListenableFuture<Boolean> processSingle(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer); SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON), return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), relationType, RelationTypeGroup.COMMON),
result -> { result -> {
if (result) { if (result) {
return processSingleDeleteRelation(ctx, sdId); return processSingleDeleteRelation(ctx, sdId);
@ -105,7 +110,7 @@ public class TbDeleteRelationNode extends TbAbstractRelationActionNode<TbDeleteR
} }
private ListenableFuture<Boolean> processSingleDeleteRelation(TbContext ctx, SearchDirectionIds sdId) { private ListenableFuture<Boolean> processSingleDeleteRelation(TbContext ctx, SearchDirectionIds sdId) {
return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON); return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), relationType, RelationTypeGroup.COMMON);
} }
} }

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNodeConfiguration.java

@ -29,7 +29,7 @@ public class TbDeleteRelationNodeConfiguration extends TbAbstractRelationActionN
TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration(); TbDeleteRelationNodeConfiguration configuration = new TbDeleteRelationNodeConfiguration();
configuration.setDeleteForSingleEntity(true); configuration.setDeleteForSingleEntity(true);
configuration.setDirection(EntitySearchDirection.FROM.name()); configuration.setDirection(EntitySearchDirection.FROM.name());
configuration.setRelationType("Contains"); configuration.setRelationTypePattern("Contains");
configuration.setEntityNamePattern(""); configuration.setEntityNamePattern("");
configuration.setEntityCacheExpiration(300); configuration.setEntityCacheExpiration(300);
return configuration; return configuration;

8
rule-engine/rule-engine-components/src/main/resources/public/static/rulenode/rulenode-core-config.js

File diff suppressed because one or more lines are too long

1
ui/src/app/locale/locale.constant-de_DE.json

@ -1559,6 +1559,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Aktionszellenschaltfläche", "action-cell-button": "Aktionszellenschaltfläche",
"row-click": "Klick auf Zeile", "row-click": "Klick auf Zeile",
"polygon-click": "Klick auf Polygon",
"marker-click": "Klick auf Marker", "marker-click": "Klick auf Marker",
"tooltip-tag-action": "Tooltip-Tag-Aktion" "tooltip-tag-action": "Tooltip-Tag-Aktion"
} }

1
ui/src/app/locale/locale.constant-en_US.json

@ -1564,6 +1564,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"marker-click": "On marker click", "marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }

3
ui/src/app/locale/locale.constant-es_ES.json

@ -1559,7 +1559,8 @@
"widget-action": { "widget-action": {
"action-cell-button": "Botón de acción de celda", "action-cell-button": "Botón de acción de celda",
"row-click": "Clic en la fila", "row-click": "Clic en la fila",
"marker-click": "Clic en el marcador", "polygon-click": "Clic en la fila",
"marker-click": "Clic en el polígono",
"tooltip-tag-action": "Acción de etiqueta para globo de ayuda" "tooltip-tag-action": "Acción de etiqueta para globo de ayuda"
} }
}, },

1
ui/src/app/locale/locale.constant-fr_FR.json

@ -336,6 +336,7 @@
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"marker-click": "On marker click", "marker-click": "On marker click",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }
}, },

1
ui/src/app/locale/locale.constant-it_IT.json

@ -1425,6 +1425,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"marker-click": "On marker click", "marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }

1
ui/src/app/locale/locale.constant-ja_JA.json

@ -1442,6 +1442,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "アクションセルボタン", "action-cell-button": "アクションセルボタン",
"row-click": "行のクリック", "row-click": "行のクリック",
"polygon-click": "ポリゴンクリック",
"marker-click": "マーカークリック", "marker-click": "マーカークリック",
"tooltip-tag-action": "ツールチップのタグアクション" "tooltip-tag-action": "ツールチップのタグアクション"
} }

1
ui/src/app/locale/locale.constant-ko_KR.json

@ -1318,6 +1318,7 @@
"widget-action": { "widget-action": {
"action-cell-button": "Action cell button", "action-cell-button": "Action cell button",
"row-click": "On row click", "row-click": "On row click",
"polygon-click": "On polygon click",
"marker-click": "On marker click", "marker-click": "On marker click",
"tooltip-tag-action": "Tooltip tag action" "tooltip-tag-action": "Tooltip tag action"
} }

1
ui/src/app/locale/locale.constant-ru_RU.json

@ -1558,6 +1558,7 @@
"action-cell-button": "Кнопка действия ячейки", "action-cell-button": "Кнопка действия ячейки",
"row-click": "Действий при щелчке на строку", "row-click": "Действий при щелчке на строку",
"marker-click": "Действия при щелчке на указателе", "marker-click": "Действия при щелчке на указателе",
"polygon-click": "Действия при щелчке на полигон",
"tooltip-tag-action": "Действие при подсказке" "tooltip-tag-action": "Действие при подсказке"
} }
}, },

3
ui/src/app/locale/locale.constant-tr_TR.json

@ -1524,7 +1524,8 @@
"widget-action": { "widget-action": {
"action-cell-button": "Eylem hücre butonu", "action-cell-button": "Eylem hücre butonu",
"row-click": "Satır tıklama eylemi", "row-click": "Satır tıklama eylemi",
"marker-click": "İşaretçi tıklama eylemi", "polygon-click": "Satır tıklama eylemi",
"marker-click": "Çokgen tıklama eylemi",
"tooltip-tag-action": "İpucu etiket eylemi" "tooltip-tag-action": "İpucu etiket eylemi"
} }
}, },

1
ui/src/app/locale/locale.constant-zh_CN.json

@ -1429,6 +1429,7 @@
"action-cell-button": "动作单元格按钮", "action-cell-button": "动作单元格按钮",
"row-click": "点击行", "row-click": "点击行",
"marker-click": "点击标记", "marker-click": "点击标记",
"polygon-click": "单击多边形",
"tooltip-tag-action": "提示标签动作" "tooltip-tag-action": "提示标签动作"
} }
}, },

28
ui/src/app/widget/lib/google-map.js

@ -315,7 +315,7 @@ export default class TbGoogleMap {
} }
createPolygon(latLangs, settings) { createPolygon(latLangs, settings, location, onClickListener, markerArgs) {
let polygon = new google.maps.Polygon({ let polygon = new google.maps.Polygon({
map: this.map, map: this.map,
paths: latLangs, paths: latLangs,
@ -325,6 +325,32 @@ export default class TbGoogleMap {
fillOpacity: settings.polygonOpacity, fillOpacity: settings.polygonOpacity,
strokeWeight: settings.polygonStrokeWeight strokeWeight: settings.polygonStrokeWeight
}); });
//initialize-tooltip
let popup = new google.maps.InfoWindow({
content: ''
});
if (!this.tooltips) this.tooltips = [];
this.tooltips.push({
markerArgs: markerArgs,
popup: popup,
locationSettings: settings,
dsIndex: location.dsIndex
});
if (onClickListener) {
google.maps.event.addListener(polygon, 'click', function (event) {
if (settings.displayTooltip) {
if (!polygon.anchor) {
polygon.anchor = new google.maps.MVCObject();
}
polygon.anchor.set("position", event.latLng);
popup.open(this.map, polygon.anchor);
}
onClickListener();
});
}
return polygon; return polygon;
} }
/* eslint-disable no-undef */ /* eslint-disable no-undef */

105
ui/src/app/widget/lib/map-widget2.js

@ -233,7 +233,6 @@ export default class TbMapWidgetV2 {
} }
update() { update() {
var tbMap = this; var tbMap = this;
@ -381,6 +380,17 @@ export default class TbMapWidgetV2 {
tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName); tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
} }
} }
function locationPolygonClick($event, location) {
var descriptors = tbMap.ctx.actionsApi.getActionDescriptors('polygonClick');
if (descriptors.length) {
var datasource = tbMap.subscription.datasources[location.dsIndex];
var entityId = {};
entityId.id = datasource.entityId;
entityId.entityType = datasource.entityType;
var entityName = datasource.entityName;
tbMap.ctx.actionsApi.handleWidgetAction($event, descriptors[0], entityId, entityName);
}
}
function updateLocation(location, data, dataMap) { function updateLocation(location, data, dataMap) {
var locationChanged = false; var locationChanged = false;
@ -427,21 +437,6 @@ export default class TbMapWidgetV2 {
locationChanged = true; locationChanged = true;
} }
} }
if (location.settings.showPolygon && dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName] !== null) {
let polygonLatLngsRaw = angular.fromJson(dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName]);
let polygonLatLngs = !polygonLatLngsRaw || mapPolygonArray(polygonLatLngsRaw);
if (!location.polygon && polygonLatLngs.length > 0) {
location.polygon = tbMap.map.createPolygon(polygonLatLngs, location.settings);
tbMap.polygons.push(location.polygon);
} else if (polygonLatLngs.length > 0) {
let prevPolygonArr = tbMap.map.getPolygonLatLngs(location.polygon);
if (!prevPolygonArr || !arraysEqual(prevPolygonArr, polygonLatLngs)) {
tbMap.map.setPolygonLatLngs(location.polygon, polygonLatLngs);
}
}
}
} }
if (location.marker) { if (location.marker) {
updateLocationStyle(location, dataMap); updateLocationStyle(location, dataMap);
@ -451,6 +446,25 @@ export default class TbMapWidgetV2 {
return locationChanged; return locationChanged;
} }
function createUpdatePolygon(location, dataMap) {
if (location.settings.showPolygon && dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName] !== null) {
let polygonLatLngsRaw = angular.fromJson(dataMap.dsDataMap[location.dsIndex][location.settings.polygonKeyName]);
let polygonLatLngs = !polygonLatLngsRaw || mapPolygonArray(polygonLatLngsRaw);
if (!location.polygon && polygonLatLngs.length > 0) {
location.polygon = tbMap.map.createPolygon(polygonLatLngs, location.settings, location, function (event) {
tbMap.callbacks.onLocationClick(location);
locationPolygonClick(event, location);
}, [location.dsIndex]);
tbMap.polygons.push(location.polygon);
} else if (polygonLatLngs.length > 0) {
let prevPolygonArr = tbMap.map.getPolygonLatLngs(location.polygon);
if (!prevPolygonArr || !arraysEqual(prevPolygonArr, polygonLatLngs)) {
tbMap.map.setPolygonLatLngs(location.polygon, polygonLatLngs);
}
}
}
}
function loadLocations(data, datasources) { function loadLocations(data, datasources) {
var bounds = tbMap.map.createBounds(); var bounds = tbMap.map.createBounds();
tbMap.locations = []; tbMap.locations = [];
@ -459,7 +473,6 @@ export default class TbMapWidgetV2 {
var currentDatasourceIndex = -1; var currentDatasourceIndex = -1;
var latIndex = -1; var latIndex = -1;
var lngIndex = -1; var lngIndex = -1;
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
var dataKeyData = data[i]; var dataKeyData = data[i];
var dataKey = dataKeyData.dataKey; var dataKey = dataKeyData.dataKey;
@ -480,7 +493,6 @@ export default class TbMapWidgetV2 {
} else if (nameToCheck === tbMap.locationSettings.lngKeyName) { } else if (nameToCheck === tbMap.locationSettings.lngKeyName) {
lngIndex = i; lngIndex = i;
} }
if (latIndex > -1 && lngIndex > -1) { if (latIndex > -1 && lngIndex > -1) {
var location = { var location = {
latIndex: latIndex, latIndex: latIndex,
@ -499,6 +511,7 @@ export default class TbMapWidgetV2 {
} }
tbMap.locations.push(location); tbMap.locations.push(location);
updateLocation(location, data, dataMap); updateLocation(location, data, dataMap);
if (location.polyline) { if (location.polyline) {
tbMap.map.extendBounds(bounds, location.polyline); tbMap.map.extendBounds(bounds, location.polyline);
} else if (location.marker) { } else if (location.marker) {
@ -507,8 +520,28 @@ export default class TbMapWidgetV2 {
latIndex = -1; latIndex = -1;
lngIndex = -1; lngIndex = -1;
} }
} }
data.forEach(function (dataEl, index) {
let nameToCheck;
if (dataEl.dataKey.locationAttrName) {
nameToCheck = dataEl.dataKey.locationAttrName;
} else {
nameToCheck = dataEl.dataKey.label;
}
if (nameToCheck === tbMap.locationSettings.polygonKeyName) {
let location = {
polIndex: index,
settings: angular.copy(tbMap.locationSettings)
};
location.dsIndex = datasources.findIndex(function (ds) {
return dataEl.datasource.entityId === ds.entityId;
});
tbMap.locations.push(location);
createUpdatePolygon(location, dataMap);
tbMap.map.extendBounds(bounds, location.polygon);
}
});
tbMap.map.fitBounds(bounds); tbMap.map.fitBounds(bounds);
} }
@ -532,10 +565,13 @@ export default class TbMapWidgetV2 {
for (var p = 0; p < tbMap.locations.length; p++) { for (var p = 0; p < tbMap.locations.length; p++) {
var location = tbMap.locations[p]; var location = tbMap.locations[p];
locationsChanged |= updateLocation(location, data, dataMap); locationsChanged |= updateLocation(location, data, dataMap);
createUpdatePolygon(location, dataMap);
if (location.polyline) { if (location.polyline) {
tbMap.map.extendBounds(bounds, location.polyline); tbMap.map.extendBounds(bounds, location.polyline);
} else if (location.marker) { } else if (location.marker) {
tbMap.map.extendBoundsWithMarker(bounds, location.marker); tbMap.map.extendBoundsWithMarker(bounds, location.marker);
} else if (location.polygon) {
tbMap.map.extendBounds(bounds, location.polygon);
} }
} }
if (locationsChanged && tbMap.initBounds) { if (locationsChanged && tbMap.initBounds) {
@ -585,23 +621,30 @@ export default class TbMapWidgetV2 {
} }
} }
} }
} }
resize() { resize() {
if (this.map && this.map.inited()) { if (this.map && this.map.inited()) {
this.map.invalidateSize(); let map = this.map;
if (this.locations && this.locations.length > 0) { if (this.locations && this.locations.length > 0) {
var bounds = this.map.createBounds(); map.invalidateSize();
for (var m = 0; m < this.markers.length; m++) { var bounds = map.createBounds();
this.map.extendBoundsWithMarker(bounds, this.markers[m]); if (this.markers && this.markers.length>0) {
this.markers.forEach(function (marker) {
map.extendBoundsWithMarker(bounds, marker);
});
} }
if (this.polylines) { if (this.polylines && this.polylines.length>0) {
for (var p = 0; p < this.polylines.length; p++) { this.polylines.forEach(function (polyline) {
this.map.extendBounds(bounds, this.polylines[p]); map.extendBounds(bounds, polyline);
} })
}
if (this.polygons && this.polygons.length > 0) {
this.polygons.forEach(function (polygon) {
map.extendBounds(bounds, polygon);
})
} }
this.map.fitBounds(bounds); map.fitBounds(bounds);
} }
} }
} }
@ -638,6 +681,10 @@ export default class TbMapWidgetV2 {
name: 'widget-action.marker-click', name: 'widget-action.marker-click',
multiple: false multiple: false
}, },
'polygonClick': {
name: 'widget-action.polygon-click',
multiple: false
},
'tooltipAction': { 'tooltipAction': {
name: 'widget-action.tooltip-tag-action', name: 'widget-action.tooltip-tag-action',
multiple: true multiple: true

12
ui/src/app/widget/lib/openstreet-map.js

@ -190,7 +190,7 @@ export default class TbOpenStreetMap {
this.map.removeLayer(polyline); this.map.removeLayer(polyline);
} }
createPolygon(latLangs, settings) { createPolygon(latLangs, settings, location, onClickListener, markerArgs) {
let polygon = L.polygon(latLangs, { let polygon = L.polygon(latLangs, {
fill: true, fill: true,
fillColor: settings.polygonColor, fillColor: settings.polygonColor,
@ -199,6 +199,13 @@ export default class TbOpenStreetMap {
fillOpacity: settings.polygonOpacity, fillOpacity: settings.polygonOpacity,
opacity: settings.polygonStrokeOpacity opacity: settings.polygonStrokeOpacity
}).addTo(this.map); }).addTo(this.map);
if (settings.displayTooltip) {
this.createTooltip(polygon, location.dsIndex, settings, markerArgs);
}
if (onClickListener) {
polygon.on('click', onClickListener);
}
return polygon; return polygon;
} }
@ -224,6 +231,7 @@ export default class TbOpenStreetMap {
setPolygonLatLngs(polygon, latLngs) { setPolygonLatLngs(polygon, latLngs) {
polygon.setLatLngs(latLngs); polygon.setLatLngs(latLngs);
polygon.redraw();
} }
fitBounds(bounds) { fitBounds(bounds) {
@ -272,7 +280,7 @@ export default class TbOpenStreetMap {
} }
extendBounds(bounds, polyline) { extendBounds(bounds, polyline) {
if (polyline && polyline.getLatLngs()) { if (polyline && polyline.getLatLngs() && polyline.getBounds()) {
bounds.extend(polyline.getBounds()); bounds.extend(polyline.getBounds());
} }
} }

25
ui/src/app/widget/lib/tencent-map.js

@ -331,7 +331,7 @@ export default class TbTencentMap {
} }
/* eslint-disable no-undef */ /* eslint-disable no-undef */
createPolygon(latLangs, settings) { createPolygon(latLangs, settings, location, onClickListener, markerArgs) {
let polygon = new qq.maps.Polygon({ let polygon = new qq.maps.Polygon({
map: this.map, map: this.map,
path: latLangs, path: latLangs,
@ -339,8 +339,31 @@ export default class TbTencentMap {
fillColor: settings.polygonColor, fillColor: settings.polygonColor,
strokeWeight: settings.polygonStrokeWeight strokeWeight: settings.polygonStrokeWeight
}); });
//initialize-tooltip
let popup = new qq.maps.InfoWindow({
content: ''
});
if (!this.tooltips) this.tooltips = [];
this.tooltips.push({
markerArgs: markerArgs,
popup: popup,
locationSettings: settings,
dsIndex: location.dsIndex
});
if (onClickListener) {
qq.maps.event.addListener(polygon, 'click', function (event) {
if (settings.displayTooltip) {
popup.setMap(this.map);
popup.setPosition(event.latLng);
popup.open();
}
onClickListener();
});
}
return polygon; return polygon;
} }
/* eslint-disable no-undef */ /* eslint-disable no-undef */
removePolygon (polygon) { removePolygon (polygon) {

34
ui/src/app/widget/lib/widget-utils.js

@ -73,22 +73,24 @@ export function processPattern(pattern, datasources, dsIndex) {
export function fillPattern(pattern, replaceInfo, data) { export function fillPattern(pattern, replaceInfo, data) {
var text = angular.copy(pattern); var text = angular.copy(pattern);
for (var v = 0; v < replaceInfo.variables.length; v++) { if (replaceInfo) {
var variableInfo = replaceInfo.variables[v]; for (var v = 0; v < replaceInfo.variables.length; v++) {
var txtVal = ''; var variableInfo = replaceInfo.variables[v];
if (variableInfo.dataKeyIndex > -1 && data[variableInfo.dataKeyIndex]) { var txtVal = '';
var varData = data[variableInfo.dataKeyIndex].data; if (variableInfo.dataKeyIndex > -1 && data[variableInfo.dataKeyIndex]) {
if (varData.length > 0) { var varData = data[variableInfo.dataKeyIndex].data;
var val = varData[varData.length - 1][1]; if (varData.length > 0) {
if (isNumber(val)) { var val = varData[varData.length - 1][1];
txtVal = padValue(val, variableInfo.valDec, 0); if (isNumber(val)) {
} else { txtVal = padValue(val, variableInfo.valDec, 0);
txtVal = val; } else {
} txtVal = val;
} }
} }
text = text.split(variableInfo.variable).join(txtVal); }
} text = text.split(variableInfo.variable).join(txtVal);
}
}
return text; return text;
} }

Loading…
Cancel
Save