Browse Source

merge with upstream

pull/1437/head
ShvaykaD 8 years ago
parent
commit
4d894ea404
  1. 4
      application/pom.xml
  2. 13
      application/src/main/java/org/thingsboard/server/controller/AlarmController.java
  3. 9
      application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithFSTService.java
  4. 2
      application/src/main/scripts/install/install_dev_db.sh
  5. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java
  6. 2
      dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java
  7. 26
      dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java
  8. 12
      dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java
  9. 5
      dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java
  10. 83
      dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java
  11. 12
      pom.xml
  12. 38
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbAbstractRelationActionNode.java
  13. 3
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java
  14. 45
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbCreateRelationNode.java
  15. 11
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbDeleteRelationNode.java
  16. 1
      ui/src/app/entity/entity-subtype-autocomplete.directive.js
  17. 2
      ui/src/app/entity/entity-subtype-autocomplete.tpl.html
  18. 1581
      ui/src/app/locale/locale.constant-de_DE.json
  19. 7
      ui/src/app/locale/locale.constant-en_US.json
  20. 5
      ui/src/app/locale/locale.constant-es_ES.json
  21. 3
      ui/src/app/locale/locale.constant-fr_FR.json
  22. 21
      ui/src/app/locale/locale.constant-it_IT.json
  23. 21
      ui/src/app/locale/locale.constant-ja_JA.json
  24. 8
      ui/src/app/locale/locale.constant-ko_KR.json
  25. 5
      ui/src/app/locale/locale.constant-ru_RU.json
  26. 3
      ui/src/app/locale/locale.constant-tr_TR.json
  27. 7
      ui/src/app/locale/locale.constant-zh_CN.json
  28. 42
      ui/src/app/widget/lib/google-map.js
  29. 15
      ui/src/app/widget/lib/image-map.js
  30. 2251
      ui/src/app/widget/lib/map-widget2.js
  31. 494
      ui/src/app/widget/lib/openstreet-map.js
  32. 786
      ui/src/app/widget/lib/tencent-map.js

4
application/pom.xml

@ -41,6 +41,10 @@
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
</dependency>
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-transport-native-epoll</artifactId> <artifactId>netty-transport-native-epoll</artifactId>

13
application/src/main/java/org/thingsboard/server/controller/AlarmController.java

@ -92,6 +92,19 @@ public class AlarmController extends BaseController {
} }
} }
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}", method = RequestMethod.DELETE)
@ResponseBody
public Boolean deleteAlarm(@PathVariable(ALARM_ID) String strAlarmId) throws ThingsboardException {
checkParameter(ALARM_ID, strAlarmId);
try {
AlarmId alarmId = new AlarmId(toUUID(strAlarmId));
return alarmService.deleteAlarm(getTenantId(), alarmId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN', 'CUSTOMER_USER')")
@RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST) @RequestMapping(value = "/alarm/{alarmId}/ack", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK) @ResponseStatus(value = HttpStatus.OK)

9
application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithJavaSerializationDecodingEncodingService.java → application/src/main/java/org/thingsboard/server/service/encoding/ProtoWithFSTService.java

@ -17,8 +17,8 @@ package org.thingsboard.server.service.encoding;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.nustaq.serialization.FSTConfiguration;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.SerializationUtils;
import org.thingsboard.server.common.msg.TbActorMsg; import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.cluster.ServerAddress; import org.thingsboard.server.common.msg.cluster.ServerAddress;
import org.thingsboard.server.gen.cluster.ClusterAPIProtos; import org.thingsboard.server.gen.cluster.ClusterAPIProtos;
@ -30,13 +30,14 @@ import static org.thingsboard.server.gen.cluster.ClusterAPIProtos.MessageType.CL
@Slf4j @Slf4j
@Service @Service
public class ProtoWithJavaSerializationDecodingEncodingService implements DataDecodingEncodingService { public class ProtoWithFSTService implements DataDecodingEncodingService {
private final FSTConfiguration config = FSTConfiguration.createDefaultConfiguration();
@Override @Override
public Optional<TbActorMsg> decode(byte[] byteArray) { public Optional<TbActorMsg> decode(byte[] byteArray) {
try { try {
TbActorMsg msg = (TbActorMsg) SerializationUtils.deserialize(byteArray); TbActorMsg msg = (TbActorMsg) config.asObject(byteArray);
return Optional.of(msg); return Optional.of(msg);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
@ -47,7 +48,7 @@ public class ProtoWithJavaSerializationDecodingEncodingService implements DataDe
@Override @Override
public byte[] encode(TbActorMsg msq) { public byte[] encode(TbActorMsg msq) {
return SerializationUtils.serialize(msq); return config.asByteArray(msq);
} }
@Override @Override

2
application/src/main/scripts/install/install_dev_db.sh

@ -28,7 +28,7 @@ export LOADER_PATH=${BASE}/conf,${BASE}/extensions
export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp} export SQL_DATA_FOLDER=${SQL_DATA_FOLDER:-/tmp}
run_user=thingsboard run_user="$USER"
sudo -u "$run_user" -s /bin/sh -c "java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \ sudo -u "$run_user" -s /bin/sh -c "java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.ThingsboardInstallApplication \
-Dinstall.data_dir=${installDir} \ -Dinstall.data_dir=${installDir} \

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmDao.java

@ -31,6 +31,8 @@ import java.util.UUID;
*/ */
public interface AlarmDao extends Dao<Alarm> { public interface AlarmDao extends Dao<Alarm> {
Boolean deleteAlarm(TenantId tenantId, Alarm alarm);
ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type); ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type);
ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key); ListenableFuture<Alarm> findAlarmByIdAsync(TenantId tenantId, UUID key);

2
dao/src/main/java/org/thingsboard/server/dao/alarm/AlarmService.java

@ -35,6 +35,8 @@ public interface AlarmService {
Alarm createOrUpdateAlarm(Alarm alarm); Alarm createOrUpdateAlarm(Alarm alarm);
Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId);
ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs); ListenableFuture<Boolean> ackAlarm(TenantId tenantId, AlarmId alarmId, long ackTs);
ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long ackTs); ListenableFuture<Boolean> clearAlarm(TenantId tenantId, AlarmId alarmId, JsonNode details, long ackTs);

26
dao/src/main/java/org/thingsboard/server/dao/alarm/BaseAlarmService.java

@ -24,6 +24,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.alarm.Alarm; import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmId; import org.thingsboard.server.common.data.alarm.AlarmId;
@ -118,6 +119,21 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
return alarmDao.findLatestByOriginatorAndType(tenantId, originator, type); return alarmDao.findLatestByOriginatorAndType(tenantId, originator, type);
} }
@Override
public Boolean deleteAlarm(TenantId tenantId, AlarmId alarmId) {
try {
log.debug("Deleting Alarm Id: {}", alarmId);
Alarm alarm = alarmDao.findAlarmByIdAsync(tenantId, alarmId.getId()).get();
if (alarm == null) {
return false;
}
deleteEntityRelations(tenantId, alarm.getId());
return alarmDao.deleteAlarm(tenantId, alarm);
} catch (ExecutionException | InterruptedException e) {
throw new RuntimeException(e);
}
}
private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException { private Alarm createAlarm(Alarm alarm) throws InterruptedException, ExecutionException {
log.debug("New Alarm : {}", alarm); log.debug("New Alarm : {}", alarm);
Alarm saved = alarmDao.save(alarm.getTenantId(), alarm); Alarm saved = alarmDao.save(alarm.getTenantId(), alarm);
@ -127,9 +143,7 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
private void createAlarmRelations(Alarm alarm) throws InterruptedException, ExecutionException { private void createAlarmRelations(Alarm alarm) throws InterruptedException, ExecutionException {
if (alarm.isPropagate()) { if (alarm.isPropagate()) {
EntityRelationsQuery query = new EntityRelationsQuery(); List<EntityId> parentEntities = getParentEntities(alarm);
query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
List<EntityId> parentEntities = relationService.findByQuery(alarm.getTenantId(), query).get().stream().map(EntityRelation::getFrom).collect(Collectors.toList());
for (EntityId parentId : parentEntities) { for (EntityId parentId : parentEntities) {
createAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), alarm.getStatus(), true); createAlarmRelation(alarm.getTenantId(), parentId, alarm.getId(), alarm.getStatus(), true);
} }
@ -137,6 +151,12 @@ public class BaseAlarmService extends AbstractEntityService implements AlarmServ
createAlarmRelation(alarm.getTenantId(), alarm.getOriginator(), alarm.getId(), alarm.getStatus(), true); createAlarmRelation(alarm.getTenantId(), alarm.getOriginator(), alarm.getId(), alarm.getStatus(), true);
} }
private List<EntityId> getParentEntities(Alarm alarm) throws InterruptedException, ExecutionException {
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(new RelationsSearchParameters(alarm.getOriginator(), EntitySearchDirection.TO, Integer.MAX_VALUE));
return relationService.findByQuery(alarm.getTenantId(), query).get().stream().map(EntityRelation::getFrom).collect(Collectors.toList());
}
private ListenableFuture<Alarm> updateAlarm(Alarm update) { private ListenableFuture<Alarm> updateAlarm(Alarm update) {
alarmDataValidator.validate(update, Alarm::getTenantId); alarmDataValidator.validate(update, Alarm::getTenantId);
return getAndUpdate(update.getTenantId(), update.getId(), new Function<Alarm, Alarm>() { return getAndUpdate(update.getTenantId(), update.getId(), new Function<Alarm, Alarm>() {

12
dao/src/main/java/org/thingsboard/server/dao/alarm/CassandraAlarmDao.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.dao.alarm; package org.thingsboard.server.dao.alarm;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
@ -78,6 +79,17 @@ public class CassandraAlarmDao extends CassandraAbstractModelDao<AlarmEntity, Al
return super.save(tenantId, alarm); return super.save(tenantId, alarm);
} }
@Override
public Boolean deleteAlarm(TenantId tenantId, Alarm alarm) {
Statement delete = QueryBuilder.delete().all().from(getColumnFamilyName()).where(eq(ModelConstants.ID_PROPERTY, alarm.getId().getId()))
.and(eq(ALARM_TENANT_ID_PROPERTY, tenantId.getId()))
.and(eq(ALARM_ORIGINATOR_ID_PROPERTY, alarm.getOriginator().getId()))
.and(eq(ALARM_ORIGINATOR_TYPE_PROPERTY, alarm.getOriginator().getEntityType()))
.and(eq(ALARM_TYPE_PROPERTY, alarm.getType()));
log.debug("Remove request: {}", delete.toString());
return executeWrite(tenantId, delete).wasApplied();
}
@Override @Override
public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
Select select = select().from(ALARM_COLUMN_FAMILY_NAME); Select select = select().from(ALARM_COLUMN_FAMILY_NAME);

5
dao/src/main/java/org/thingsboard/server/dao/sql/alarm/JpaAlarmDao.java

@ -68,6 +68,11 @@ public class JpaAlarmDao extends JpaAbstractDao<AlarmEntity, Alarm> implements A
return alarmRepository; return alarmRepository;
} }
@Override
public Boolean deleteAlarm(TenantId tenantId, Alarm alarm) {
return removeById(tenantId, alarm.getUuidId());
}
@Override @Override
public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) { public ListenableFuture<Alarm> findLatestByOriginatorAndType(TenantId tenantId, EntityId originator, String type) {
return service.submit(() -> { return service.submit(() -> {

83
dao/src/test/java/org/thingsboard/server/dao/service/BaseAlarmServiceTest.java

@ -31,7 +31,9 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.TimePageData; import org.thingsboard.server.common.data.page.TimePageData;
import org.thingsboard.server.common.data.page.TimePageLink; import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import java.util.List;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
public abstract class BaseAlarmServiceTest extends AbstractServiceTest { public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
@ -184,4 +186,85 @@ public abstract class BaseAlarmServiceTest extends AbstractServiceTest {
Assert.assertEquals(1, alarms.getData().size()); Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0)); Assert.assertEquals(created, alarms.getData().get(0));
} }
@Test
public void testDeleteAlarm() throws ExecutionException, InterruptedException {
AssetId parentId = new AssetId(UUIDs.timeBased());
AssetId childId = new AssetId(UUIDs.timeBased());
EntityRelation relation = new EntityRelation(parentId, childId, EntityRelation.CONTAINS_TYPE);
Assert.assertTrue(relationService.saveRelationAsync(tenantId, relation).get());
long ts = System.currentTimeMillis();
Alarm alarm = Alarm.builder().tenantId(tenantId).originator(childId)
.type(TEST_ALARM)
.propagate(true)
.severity(AlarmSeverity.CRITICAL).status(AlarmStatus.ACTIVE_UNACK)
.startTs(ts).build();
Alarm created = alarmService.createOrUpdateAlarm(alarm);
TimePageData<AlarmInfo> alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
// Check parent relation
alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(parentId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(1, alarms.getData().size());
Assert.assertEquals(created, alarms.getData().get(0));
List<EntityRelation> toAlarmRelations = relationService.findByTo(tenantId, created.getId(), RelationTypeGroup.ALARM);
Assert.assertEquals(8, toAlarmRelations.size());
List<EntityRelation> fromChildRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(4, fromChildRelations.size());
List<EntityRelation> fromParentRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(4, fromParentRelations.size());
Assert.assertTrue("Alarm was not deleted when expected", alarmService.deleteAlarm(tenantId, created.getId()));
Alarm fetched = alarmService.findAlarmByIdAsync(tenantId, created.getId()).get();
Assert.assertNull("Alarm was returned when it was expected to be null", fetched);
alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(childId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size());
// Check parent relation
alarms = alarmService.findAlarms(tenantId, AlarmQuery.builder()
.affectedEntityId(parentId)
.status(AlarmStatus.ACTIVE_UNACK).pageLink(
new TimePageLink(1, 0L, System.currentTimeMillis(), false)
).build()).get();
Assert.assertNotNull(alarms.getData());
Assert.assertEquals(0, alarms.getData().size());
toAlarmRelations = relationService.findByTo(tenantId, created.getId(), RelationTypeGroup.ALARM);
Assert.assertEquals(0, toAlarmRelations.size());
fromChildRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(0, fromChildRelations.size());
fromParentRelations = relationService.findByFrom(tenantId, childId, RelationTypeGroup.ALARM);
Assert.assertEquals(0, fromParentRelations.size());
}
} }

12
pom.xml

@ -84,6 +84,7 @@
<delight-nashorn-sandbox.version>0.1.14</delight-nashorn-sandbox.version> <delight-nashorn-sandbox.version>0.1.14</delight-nashorn-sandbox.version>
<kafka.version>2.0.0</kafka.version> <kafka.version>2.0.0</kafka.version>
<bucket4j.version>4.1.1</bucket4j.version> <bucket4j.version>4.1.1</bucket4j.version>
<fst.version>2.57</fst.version>
</properties> </properties>
<modules> <modules>
@ -756,12 +757,6 @@
<artifactId>hsqldb</artifactId> <artifactId>hsqldb</artifactId>
<version>${hsqldb.version}</version> <version>${hsqldb.version}</version>
</dependency> </dependency>
<dependency>
<groupId>ru.yandex.qatools.embed</groupId>
<artifactId>postgresql-embedded</artifactId>
<version>2.2</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.data</groupId> <groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId> <artifactId>spring-data-redis</artifactId>
@ -795,6 +790,11 @@
<artifactId>bucket4j-core</artifactId> <artifactId>bucket4j-core</artifactId>
<version>${bucket4j.version}</version> <version>${bucket4j.version}</version>
</dependency> </dependency>
<dependency>
<groupId>de.ruedigermoeller</groupId>
<artifactId>fst</artifactId>
<version>${fst.version}</version>
</dependency>
<dependency> <dependency>
<groupId>io.springfox.ui</groupId> <groupId>io.springfox.ui</groupId>
<artifactId>springfox-swagger-ui-rfc6570</artifactId> <artifactId>springfox-swagger-ui-rfc6570</artifactId>

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

@ -61,10 +61,8 @@ import static org.thingsboard.rule.engine.api.util.DonAsynchron.withCallback;
public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationActionNodeConfiguration> implements TbNode { public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationActionNodeConfiguration> implements TbNode {
protected C config; protected C config;
protected EntityId fromId;
protected EntityId toId;
private LoadingCache<Entitykey, EntityContainer> entityIdCache; private LoadingCache<EntityKey, EntityContainer> entityIdCache;
@Override @Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
@ -99,29 +97,33 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
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 = TbNodeUtils.processPattern(this.config.getEntityNamePattern(), msg.getMetaData());
String type = null; String type;
if (this.config.getEntityTypePattern() != null) { if (this.config.getEntityTypePattern() != null) {
type = TbNodeUtils.processPattern(this.config.getEntityTypePattern(), msg.getMetaData()); type = TbNodeUtils.processPattern(this.config.getEntityTypePattern(), msg.getMetaData());
} else {
type = null;
} }
EntityType entityType = EntityType.valueOf(this.config.getEntityType()); EntityType entityType = EntityType.valueOf(this.config.getEntityType());
Entitykey key = new Entitykey(entityName, type, entityType); EntityKey key = new EntityKey(entityName, type, entityType);
return ctx.getDbCallbackExecutor().executeAsync(() -> { return ctx.getDbCallbackExecutor().executeAsync(() -> {
EntityContainer entityContainer = entityIdCache.get(key); EntityContainer entityContainer = entityIdCache.get(key);
if (entityContainer.getEntityId() == null) { if (entityContainer.getEntityId() == null) {
throw new RuntimeException("No entity found with type '" + key.getEntityType() + " ' and name '" + key.getEntityName() + "'."); throw new RuntimeException("No entity found with type '" + key.getEntityType() + "' and name '" + key.getEntityName() + "'.");
} }
return entityContainer; return entityContainer;
}); });
} }
protected void processSingleSearchDirection(TbMsg msg, EntityContainer entityContainer) { protected SearchDirectionIds processSingleSearchDirection(TbMsg msg, EntityContainer entityContainer) {
SearchDirectionIds searchDirectionIds = new SearchDirectionIds();
if (EntitySearchDirection.FROM.name().equals(config.getDirection())) { if (EntitySearchDirection.FROM.name().equals(config.getDirection())) {
fromId = EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()); searchDirectionIds.setFromId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()));
toId = msg.getOriginator(); searchDirectionIds.setToId(msg.getOriginator());
} else { } else {
toId = EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()); searchDirectionIds.setToId(EntityIdFactory.getByTypeAndId(entityContainer.getEntityType().name(), entityContainer.getEntityId().toString()));
fromId = msg.getOriginator(); searchDirectionIds.setFromId(msg.getOriginator());
} }
return searchDirectionIds;
} }
protected ListenableFuture<List<EntityRelation>> processListSearchDirection(TbContext ctx, TbMsg msg) { protected ListenableFuture<List<EntityRelation>> processListSearchDirection(TbContext ctx, TbMsg msg) {
@ -134,13 +136,19 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
@Data @Data
@AllArgsConstructor @AllArgsConstructor
private static class Entitykey { private static class EntityKey {
private String entityName; private String entityName;
private String type; private String type;
private EntityType entityType; private EntityType entityType;
} }
private static class EntityCacheLoader extends CacheLoader<Entitykey, EntityContainer> { @Data
protected static class SearchDirectionIds {
private EntityId fromId;
private EntityId toId;
}
private static class EntityCacheLoader extends CacheLoader<EntityKey, EntityContainer> {
private final TbContext ctx; private final TbContext ctx;
private final boolean createIfNotExists; private final boolean createIfNotExists;
@ -151,11 +159,11 @@ public abstract class TbAbstractRelationActionNode<C extends TbAbstractRelationA
} }
@Override @Override
public EntityContainer load(Entitykey key) { public EntityContainer load(EntityKey key) {
return loadEntity(key); return loadEntity(key);
} }
private EntityContainer loadEntity(Entitykey entitykey) { private EntityContainer loadEntity(EntityKey entitykey) {
EntityType type = entitykey.getEntityType(); EntityType type = entitykey.getEntityType();
EntityContainer targetEntity = new EntityContainer(); EntityContainer targetEntity = new EntityContainer();
targetEntity.setEntityType(type); targetEntity.setEntityType(type);

3
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbClearAlarmNode.java

@ -54,7 +54,8 @@ public class TbClearAlarmNode extends TbAbstractAlarmNode<TbClearAlarmNodeConfig
@Override @Override
protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) { protected ListenableFuture<AlarmResult> processAlarm(TbContext ctx, TbMsg msg) {
ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), config.getAlarmType()); String alarmType = TbNodeUtils.processPattern(this.config.getAlarmType(), msg.getMetaData());
ListenableFuture<Alarm> latest = ctx.getAlarmService().findLatestByOriginatorAndType(ctx.getTenantId(), msg.getOriginator(), alarmType);
return Futures.transformAsync(latest, a -> { return Futures.transformAsync(latest, a -> {
if (a != null && !a.getStatus().isCleared()) { if (a != null && !a.getStatus().isCleared()) {
return clearAlarm(ctx, msg, a); return clearAlarm(ctx, msg, a);

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

@ -65,92 +65,91 @@ public class TbCreateRelationNode extends TbAbstractRelationActionNode<TbCreateR
} }
private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) { private ListenableFuture<Boolean> createIfAbsent(TbContext ctx, TbMsg msg, EntityContainer entityContainer) {
processSingleSearchDirection(msg, entityContainer); SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON), return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON),
result -> { result -> {
if (!result) { if (!result) {
return processCreateRelation(ctx, entityContainer); return processCreateRelation(ctx, entityContainer, sdId);
} }
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
}); });
} }
private ListenableFuture<Boolean> processCreateRelation(TbContext ctx, EntityContainer entityContainer) { private ListenableFuture<Boolean> processCreateRelation(TbContext ctx, EntityContainer entityContainer, SearchDirectionIds sdId) {
switch (entityContainer.getEntityType()) { switch (entityContainer.getEntityType()) {
case ASSET: case ASSET:
return processAsset(ctx, entityContainer); return processAsset(ctx, entityContainer, sdId);
case DEVICE: case DEVICE:
return processDevice(ctx, entityContainer); return processDevice(ctx, entityContainer, sdId);
case CUSTOMER: case CUSTOMER:
return processCustomer(ctx, entityContainer); return processCustomer(ctx, entityContainer, sdId);
case DASHBOARD: case DASHBOARD:
return processDashboard(ctx, entityContainer); return processDashboard(ctx, entityContainer, sdId);
case ENTITY_VIEW: case ENTITY_VIEW:
return processView(ctx, entityContainer); return processView(ctx, entityContainer, sdId);
case TENANT: case TENANT:
return processTenant(ctx, entityContainer); return processTenant(ctx, entityContainer, sdId);
} }
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
private ListenableFuture<Boolean> processView(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
private ListenableFuture<Boolean> processDevice(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
private ListenableFuture<Boolean> processAsset(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
private ListenableFuture<Boolean> processCustomer(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
private ListenableFuture<Boolean> processDashboard(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
private ListenableFuture<Boolean> processTenant(TbContext ctx, EntityContainer entityContainer) { 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(fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON)); return ctx.getRelationService().saveRelationAsync(ctx.getTenantId(), new EntityRelation(sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON));
} else { } else {
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
} }
}); });
} }
} }

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

@ -93,18 +93,19 @@ 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) {
processSingleSearchDirection(msg, entityContainer); SearchDirectionIds sdId = processSingleSearchDirection(msg, entityContainer);
return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON), return Futures.transformAsync(ctx.getRelationService().checkRelation(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON),
result -> { result -> {
if (result) { if (result) {
return processSingleDeleteRelation(ctx); return processSingleDeleteRelation(ctx, sdId);
} }
return Futures.immediateFuture(true); return Futures.immediateFuture(true);
}); });
} }
private ListenableFuture<Boolean> processSingleDeleteRelation(TbContext ctx) { private ListenableFuture<Boolean> processSingleDeleteRelation(TbContext ctx, SearchDirectionIds sdId) {
return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), fromId, toId, config.getRelationType(), RelationTypeGroup.COMMON); return ctx.getRelationService().deleteRelationAsync(ctx.getTenantId(), sdId.getFromId(), sdId.getToId(), config.getRelationType(), RelationTypeGroup.COMMON);
} }
} }

1
ui/src/app/entity/entity-subtype-autocomplete.directive.js

@ -62,6 +62,7 @@ export default function EntitySubtypeAutocomplete($compile, $templateCache, $q,
} }
scope.subTypeSearchTextChanged = function() { scope.subTypeSearchTextChanged = function() {
scope.subType = scope.subTypeSearchText;
} }
scope.updateView = function () { scope.updateView = function () {

2
ui/src/app/entity/entity-subtype-autocomplete.tpl.html

@ -28,7 +28,7 @@
md-min-length="0" md-min-length="0"
placeholder="{{ selectEntitySubtypeText | translate }}" placeholder="{{ selectEntitySubtypeText | translate }}"
md-floating-label="{{ entitySubtypeText | translate }}" md-floating-label="{{ entitySubtypeText | translate }}"
md-select-on-match="true" md-select-on-match="false"
md-menu-class="tb-entity-subtype-autocomplete"> md-menu-class="tb-entity-subtype-autocomplete">
<md-item-template> <md-item-template>
<div class="tb-entity-subtype-item"> <div class="tb-entity-subtype-item">

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

File diff suppressed because it is too large

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

@ -1034,7 +1034,6 @@
"modbus-register-count": "Register count", "modbus-register-count": "Register count",
"modbus-register-count-range": "Register count should be a positive value.", "modbus-register-count-range": "Register count should be a positive value.",
"modbus-byte-order": "Byte order", "modbus-byte-order": "Byte order",
"sync": { "sync": {
"status": "Status", "status": "Status",
"sync": "Sync", "sync": "Sync",
@ -1042,7 +1041,6 @@
"last-sync-time": "Last sync time", "last-sync-time": "Last sync time",
"not-available": "Not available" "not-available": "Not available"
}, },
"export-extensions-configuration": "Export extensions configuration", "export-extensions-configuration": "Export extensions configuration",
"import-extensions-configuration": "Import extensions configuration", "import-extensions-configuration": "Import extensions configuration",
"import-extensions": "Import extensions", "import-extensions": "Import extensions",
@ -1568,7 +1566,8 @@
"language": { "language": {
"language": "Language", "language": "Language",
"locales": { "locales": {
"fr_FR": "French", "de_DE": "German",
"fr_FR": "French",
"zh_CN": "Chinese", "zh_CN": "Chinese",
"en_US": "English", "en_US": "English",
"it_IT": "Italian", "it_IT": "Italian",
@ -1579,4 +1578,4 @@
"tr_TR": "Turkish" "tr_TR": "Turkish"
} }
} }
} }

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

@ -1034,7 +1034,6 @@
"modbus-register-count": "Contador del registro", "modbus-register-count": "Contador del registro",
"modbus-register-count-range": "Contador del registro debe ser un valor positivo.", "modbus-register-count-range": "Contador del registro debe ser un valor positivo.",
"modbus-byte-order": "Orden del byte", "modbus-byte-order": "Orden del byte",
"sync": { "sync": {
"status": "Estado", "status": "Estado",
"sync": "Sincronización", "sync": "Sincronización",
@ -1042,7 +1041,6 @@
"last-sync-time": "Último tiempo de sincroniación", "last-sync-time": "Último tiempo de sincroniación",
"not-available": "No disponible" "not-available": "No disponible"
}, },
"export-extensions-configuration": "Exportar configuración de extensiones", "export-extensions-configuration": "Exportar configuración de extensiones",
"import-extensions-configuration": "Importar configuración de extensiones", "import-extensions-configuration": "Importar configuración de extensiones",
"import-extensions": "Importar extensiones", "import-extensions": "Importar extensiones",
@ -1568,6 +1566,7 @@
"language": { "language": {
"language": "Lenguaje", "language": "Lenguaje",
"locales": { "locales": {
"de_DE": "Alemán",
"fr_FR": "Francés", "fr_FR": "Francés",
"zh_CN": "Chino", "zh_CN": "Chino",
"en_US": "Inglés", "en_US": "Inglés",
@ -1579,4 +1578,4 @@
"tr_TR": "Turco" "tr_TR": "Turco"
} }
} }
} }

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

@ -1003,6 +1003,7 @@
"language": { "language": {
"language": "Language", "language": "Language",
"locales": { "locales": {
"de_DE": "Allemand",
"en_US": "Anglais", "en_US": "Anglais",
"fr_FR": "Français", "fr_FR": "Français",
"es_ES": "Espagnol", "es_ES": "Espagnol",
@ -1459,4 +1460,4 @@
"widgets-bundle-required": "Un groupe de widgets est requis.", "widgets-bundle-required": "Un groupe de widgets est requis.",
"widgets-bundles": "Groupes de widgets" "widgets-bundles": "Groupes de widgets"
} }
} }

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

@ -100,7 +100,7 @@
"UNACK": "Non riconosciuto" "UNACK": "Non riconosciuto"
}, },
"display-status": { "display-status": {
"ACTIVE_UNACK": "Active Unacknowledged", "ACTIVE_UNACK": "Active Unacknowledged",
"ACTIVE_ACK": "Active Acknowledged", "ACTIVE_ACK": "Active Acknowledged",
"CLEARED_UNACK": "Cleared Unacknowledged", "CLEARED_UNACK": "Cleared Unacknowledged",
"CLEARED_ACK": "Cleared Acknowledged" "CLEARED_ACK": "Cleared Acknowledged"
@ -109,8 +109,8 @@
"created-time": "Orario di creazione", "created-time": "Orario di creazione",
"type": "Tipo", "type": "Tipo",
"severity": "Livello di gravità", "severity": "Livello di gravità",
"originator": "Origine", "originator": "Origine",
"originator-type": "Tipo origine", "originator-type": "Tipo origine",
"details": "Dettagli", "details": "Dettagli",
"status": "Stato", "status": "Stato",
"alarm-details": "Dettagli allarme", "alarm-details": "Dettagli allarme",
@ -132,9 +132,9 @@
"polling-interval-required": "Intervallo di polling Allarmi richiesto.", "polling-interval-required": "Intervallo di polling Allarmi richiesto.",
"min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.", "min-polling-interval-message": "L'intervallo di polling deve essere di almeno 1 sec.",
"aknowledge-alarms-title": "Conferma { count, plural, 1 {1 allarme} other {# allarmi} }", "aknowledge-alarms-title": "Conferma { count, plural, 1 {1 allarme} other {# allarmi} }",
"aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, 1 {1 allarme} other {# allarmi} }?", "aknowledge-alarms-text": "Sei sicuro di voler confermare { count, plural, 1 {1 allarme} other {# allarmi} }?",
"clear-alarms-title": "Elimina { count, plural, 1 {1 allarme} other {# allarmi} }", "clear-alarms-title": "Elimina { count, plural, 1 {1 allarme} other {# allarmi} }",
"clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, 1 {1 allarme} other {# allarmi} }?" "clear-alarms-text": "Sei sicuro di voler eliminare { count, plural, 1 {1 allarme} other {# allarmi} }?"
}, },
"alias": { "alias": {
"add": "Aggiungi alias", "add": "Aggiungi alias",
@ -910,7 +910,6 @@
"modbus-register-count": "Register count", "modbus-register-count": "Register count",
"modbus-register-count-range": "Register count should be a positive value.", "modbus-register-count-range": "Register count should be a positive value.",
"modbus-byte-order": "Byte order", "modbus-byte-order": "Byte order",
"sync": { "sync": {
"status": "Stato", "status": "Stato",
"sync": "Sincronizzato", "sync": "Sincronizzato",
@ -918,7 +917,6 @@
"last-sync-time": "Ultima sincronizzazione", "last-sync-time": "Ultima sincronizzazione",
"not-available": "Non disponibile" "not-available": "Non disponibile"
}, },
"export-extensions-configuration": "Esporta configurazione estensioni", "export-extensions-configuration": "Esporta configurazione estensioni",
"import-extensions-configuration": "Importa configurazione estensioni", "import-extensions-configuration": "Importa configurazione estensioni",
"import-extensions": "Importa estensione", "import-extensions": "Importa estensione",
@ -1414,7 +1412,7 @@
"export": "Esporta un tipo di widget", "export": "Esporta un tipo di widget",
"export-failed-error": "Impossibile esportare il tipo di widget: {{error}}", "export-failed-error": "Impossibile esportare il tipo di widget: {{error}}",
"create-new-widget-type": "Crea un nuovo tipo di widget", "create-new-widget-type": "Crea un nuovo tipo di widget",
"widget-type-file": "Widget type file", "widget-type-file": "Widget type file",
"invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida." "invalid-widget-type-file-error": "Impossibile importare un tipo di widget: struttura dati del widget non valida."
}, },
"icon": { "icon": {
@ -1424,7 +1422,7 @@
"show-all": "Mostra tutte le icone" "show-all": "Mostra tutte le icone"
}, },
"custom": { "custom": {
"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",
"marker-click": "On marker click", "marker-click": "On marker click",
@ -1434,7 +1432,8 @@
"language": { "language": {
"language": "Lingua", "language": "Lingua",
"locales": { "locales": {
"fr_FR": "Francese", "de_DE": "Tedesco",
"fr_FR": "Francese",
"zh_CN": "Cinese", "zh_CN": "Cinese",
"ko_KR": "Coreano", "ko_KR": "Coreano",
"en_US": "Inglese", "en_US": "Inglese",
@ -1445,4 +1444,4 @@
"tr_TR": "Turco" "tr_TR": "Turco"
} }
} }
} }

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

@ -288,11 +288,11 @@
"type-suspended": "一時停止中", "type-suspended": "一時停止中",
"type-credentials-read": "信用証明書を読む", "type-credentials-read": "信用証明書を読む",
"type-attributes-read": "読み取られた属性", "type-attributes-read": "読み取られた属性",
"type-relation-add-or-update": "関係が更新されました", "type-relation-add-or-update": "関係が更新されました",
"type-relation-delete": "関係が削除されました", "type-relation-delete": "関係が削除されました",
"type-relations-delete": "すべてのリレーションを削除", "type-relations-delete": "すべてのリレーションを削除",
"type-alarm-ack": "承認された", "type-alarm-ack": "承認された",
"type-alarm-clear": "クリアされた", "type-alarm-clear": "クリアされた",
"status-success": "成功", "status-success": "成功",
"status-failure": "失敗", "status-failure": "失敗",
"audit-log-details": "監査ログの詳細", "audit-log-details": "監査ログの詳細",
@ -322,7 +322,7 @@
"common": { "common": {
"username": "ユーザー名", "username": "ユーザー名",
"password": "パスワード", "password": "パスワード",
"enter-username": "ユーザーネームを入力してください", "enter-username": "ユーザーネームを入力してください",
"enter-password": "パスワードを入力する", "enter-password": "パスワードを入力する",
"enter-search": "検索を入力" "enter-search": "検索を入力"
}, },
@ -919,7 +919,6 @@
"modbus-register-count": "レジスタ数", "modbus-register-count": "レジスタ数",
"modbus-register-count-range": "レジスタ数は正の値でなければなりません。", "modbus-register-count-range": "レジスタ数は正の値でなければなりません。",
"modbus-byte-order": "バイト順", "modbus-byte-order": "バイト順",
"sync": { "sync": {
"status": "状態", "status": "状態",
"sync": "同期", "sync": "同期",
@ -927,7 +926,6 @@
"last-sync-time": "前回の同期時間", "last-sync-time": "前回の同期時間",
"not-available": "利用不可" "not-available": "利用不可"
}, },
"export-extensions-configuration": "エクステンション設定のエクスポート", "export-extensions-configuration": "エクステンション設定のエクスポート",
"import-extensions-configuration": "エクステンション設定のインポート", "import-extensions-configuration": "エクステンション設定のインポート",
"import-extensions": "拡張機能のインポート", "import-extensions": "拡張機能のインポート",
@ -1417,7 +1415,7 @@
"action-source-required": "アクションソースが必要です。", "action-source-required": "アクションソースが必要です。",
"action-name": "名", "action-name": "名",
"action-name-required": "アクション名は必須です。", "action-name-required": "アクション名は必須です。",
"action-name-not-unique": "同じ名前の別のアクションがすでに存在します。<br/>アクション名は、同じアクションソース内で一意である必要があります。", "action-name-not-unique": "同じ名前の別のアクションがすでに存在します。<br/>アクション名は、同じアクションソース内で一意である必要があります。",
"action-icon": "アイコン", "action-icon": "アイコン",
"action-type": "タイプ", "action-type": "タイプ",
"action-type-required": "アクションタイプが必要です。", "action-type-required": "アクションタイプが必要です。",
@ -1427,7 +1425,7 @@
"delete-action-text": "'{{actionName}}'?" "delete-action-text": "'{{actionName}}'?"
}, },
"widget-type": { "widget-type": {
"import": "インポートウィジェットタイプ", "import": "インポートウィジェットタイプ",
"export": "ウィジェットのタイプをエクスポートする", "export": "ウィジェットのタイプをエクスポートする",
"export-failed-error": "{{error}}", "export-failed-error": "{{error}}",
"create-new-widget-type": "新しいウィジェットタイプを作成する", "create-new-widget-type": "新しいウィジェットタイプを作成する",
@ -1451,6 +1449,7 @@
"language": { "language": {
"language": "言語", "language": "言語",
"locales": { "locales": {
"de_DE": "ドイツ語",
"en_US": "英語", "en_US": "英語",
"ko_KR": "韓国語", "ko_KR": "韓国語",
"it_IT": "イタリアの", "it_IT": "イタリアの",
@ -1461,4 +1460,4 @@
"tr_TR": "トルコ語" "tr_TR": "トルコ語"
} }
} }
} }

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

@ -85,7 +85,6 @@
"enable-tls": "TLS 사용", "enable-tls": "TLS 사용",
"send-test-mail": "테스트 메일 보내기" "send-test-mail": "테스트 메일 보내기"
}, },
"alarm": { "alarm": {
"alarm": "Alarm", "alarm": "Alarm",
"alarms": "Alarms", "alarms": "Alarms",
@ -831,7 +830,6 @@
"modbus-register-count": "Register count", "modbus-register-count": "Register count",
"modbus-register-count-range": "Register count should be a positive value.", "modbus-register-count-range": "Register count should be a positive value.",
"modbus-byte-order": "Byte order", "modbus-byte-order": "Byte order",
"sync": { "sync": {
"status": "Status", "status": "Status",
"sync": "Sync", "sync": "Sync",
@ -839,7 +837,6 @@
"last-sync-time": "Last sync time", "last-sync-time": "Last sync time",
"not-available": "Not available" "not-available": "Not available"
}, },
"export-extensions-configuration": "Export extensions configuration", "export-extensions-configuration": "Export extensions configuration",
"import-extensions-configuration": "Import extensions configuration", "import-extensions-configuration": "Import extensions configuration",
"import-extensions": "Import extensions", "import-extensions": "Import extensions",
@ -1328,8 +1325,9 @@
"language": { "language": {
"language": "언어", "language": "언어",
"locales": { "locales": {
"de_DE": "독일어",
"en_US": "영어", "en_US": "영어",
"fr_FR": "프랑스의", "fr_FR": "프랑스의",
"ko_KR": "한글", "ko_KR": "한글",
"zh_CN": "중국어", "zh_CN": "중국어",
"ru_RU": "러시아어", "ru_RU": "러시아어",
@ -1339,4 +1337,4 @@
"tr_TR": "터키어" "tr_TR": "터키어"
} }
} }
} }

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

@ -1032,7 +1032,6 @@
"modbus-register-count": "Количество регистров", "modbus-register-count": "Количество регистров",
"modbus-register-count-range": "Количество регистров должно быть больше ноля.", "modbus-register-count-range": "Количество регистров должно быть больше ноля.",
"modbus-byte-order": "Порядок байтов", "modbus-byte-order": "Порядок байтов",
"sync": { "sync": {
"status": "Статус", "status": "Статус",
"sync": "Синхронизирован", "sync": "Синхронизирован",
@ -1040,7 +1039,6 @@
"last-sync-time": "Время последней синхронизации", "last-sync-time": "Время последней синхронизации",
"not-available": "Не доступен" "not-available": "Не доступен"
}, },
"export-extensions-configuration": "Экспортировать конфигурацию расширений", "export-extensions-configuration": "Экспортировать конфигурацию расширений",
"import-extensions-configuration": "Импортировать конфигурацию расширений", "import-extensions-configuration": "Импортировать конфигурацию расширений",
"import-extensions": "Импортировать расширения", "import-extensions": "Импортировать расширения",
@ -1566,6 +1564,7 @@
"language": { "language": {
"language": "Язык", "language": "Язык",
"locales": { "locales": {
"de_DE": "Немецкий",
"en_US": "Английский", "en_US": "Английский",
"zh_CN": "Китайский", "zh_CN": "Китайский",
"ko_KR": "Корейский", "ko_KR": "Корейский",
@ -1577,4 +1576,4 @@
"ja_JA": "Японский" "ja_JA": "Японский"
} }
} }
} }

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

@ -1175,7 +1175,7 @@
"delete-rulechains-action-title": "{ count, plural, 1 {1 kuralı} other {# kuralı} } sil", "delete-rulechains-action-title": "{ count, plural, 1 {1 kuralı} other {# kuralı} } sil",
"delete-rulechains-text": "UYARI: Onaylandıktan sonra seçili tüm kurallar ve ilişkili tüm veriler geri yüklenemez şekilde silinecektir.", "delete-rulechains-text": "UYARI: Onaylandıktan sonra seçili tüm kurallar ve ilişkili tüm veriler geri yüklenemez şekilde silinecektir.",
"add-rulechain-text": "Yeni kural ekle", "add-rulechain-text": "Yeni kural ekle",
"no-rulechains-text":"Hiçbir kural bulunamadı", "no-rulechains-text": "Hiçbir kural bulunamadı",
"rulechain-details": "Kural detayları", "rulechain-details": "Kural detayları",
"details": "Detaylar", "details": "Detaylar",
"events": "Olaylar", "events": "Olaylar",
@ -1531,6 +1531,7 @@
"language": { "language": {
"language": "Dil", "language": "Dil",
"locales": { "locales": {
"de_DE": "Almanca",
"fr_FR": "Fransızca", "fr_FR": "Fransızca",
"zh_CN": "Çince", "zh_CN": "Çince",
"en_US": "İngilizce", "en_US": "İngilizce",

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

@ -916,7 +916,6 @@
"last-sync-time": "最后同步时间", "last-sync-time": "最后同步时间",
"not-available": "无法使用" "not-available": "无法使用"
}, },
"export-extensions-configuration": "导出扩展配置", "export-extensions-configuration": "导出扩展配置",
"import-extensions-configuration": "导入扩展配置", "import-extensions-configuration": "导入扩展配置",
"import-extensions": "导入扩展", "import-extensions": "导入扩展",
@ -1299,7 +1298,6 @@
"saveAs": "部件另存为", "saveAs": "部件另存为",
"save-widget-type-as": "部件类型另存为", "save-widget-type-as": "部件类型另存为",
"save-widget-type-as-text": "请输入新的部件标题或选择目标部件包", "save-widget-type-as-text": "请输入新的部件标题或选择目标部件包",
"toggle-fullscreen": "切换全屏", "toggle-fullscreen": "切换全屏",
"run": "运行部件", "run": "运行部件",
"title": "部件标题", "title": "部件标题",
@ -1437,8 +1435,9 @@
"language": { "language": {
"language": "语言", "language": "语言",
"locales": { "locales": {
"de_DE": "德语",
"en_US": "英语", "en_US": "英语",
"fr_FR": "法国", "fr_FR": "法国",
"ko_KR": "韩语", "ko_KR": "韩语",
"zh_CN": "汉语", "zh_CN": "汉语",
"ru_RU": "俄语", "ru_RU": "俄语",
@ -1448,4 +1447,4 @@
"tr_TR": "土耳其" "tr_TR": "土耳其"
} }
} }
} }

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

@ -314,6 +314,48 @@ export default class TbGoogleMap {
polyline.setMap(null); polyline.setMap(null);
} }
createPolygon(latLangs, settings) {
let polygon = new google.maps.Polygon({
map: this.map,
paths: latLangs,
strokeColor: settings.polygonStrokeColor,
strokeOpacity: settings.polygonStrokeColor,
fillColor: settings.polygonColor,
fillOpacity: settings.polygonOpacity,
strokeWeight: settings.polygonStrokeWeight
});
return polygon;
}
/* eslint-disable no-undef */
removePolygon (polygon) {
polygon.setMap(null);
}
/* eslint-disable no-undef,no-unused-vars */
updatePolygonColor (polygon, settings, color) {
let options = {
paths: polygon.getPaths(),
map: this.map,
strokeColor: color,
fillColor: color,
strokeWeight: settings.polygonStrokeWeight
}
}
/* eslint-disable no-undef ,no-unused-vars*/
getPolygonLatLngs(polygon) {
return polygon.getPaths().getArray();
}
setPolygonLatLngs(polygon, latLngs) {
polygon.setPaths(latLngs);
}
/* eslint-disable no-undef */ /* eslint-disable no-undef */
fitBounds(bounds) { fitBounds(bounds) {
if (this.dontFitMapBounds && this.defaultZoomLevel) { if (this.dontFitMapBounds && this.defaultZoomLevel) {

15
ui/src/app/widget/lib/image-map.js

@ -369,6 +369,21 @@ export default class TbImageMap {
removePolyline(/*polyline*/) { removePolyline(/*polyline*/) {
} }
createPolygon(/*latLangs, settings*/) {
}
removePolygon(/*latLangs, settings*/) {
}
updatePolygonColor(/*latLangs, settings*/) {
}
getPolygonLatLngs(/*latLangs, settings*/) {
}
setPolygonLatLngs(/*latLangs, settings*/) {
}
fitBounds() { fitBounds() {
} }

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

File diff suppressed because it is too large

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

@ -19,234 +19,270 @@ import 'leaflet-providers';
export default class TbOpenStreetMap { export default class TbOpenStreetMap {
constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) { constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, mapProvider) {
this.utils = utils; this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel; this.defaultZoomLevel = defaultZoomLevel;
this.dontFitMapBounds = dontFitMapBounds; this.dontFitMapBounds = dontFitMapBounds;
this.minZoomLevel = minZoomLevel; this.minZoomLevel = minZoomLevel;
this.tooltips = []; this.tooltips = [];
if (!mapProvider) { if (!mapProvider) {
mapProvider = "OpenStreetMap.Mapnik"; mapProvider = "OpenStreetMap.Mapnik";
} }
this.map = L.map($containerElement[0]).setView([0, 0], this.defaultZoomLevel || 8); this.map = L.map($containerElement[0]).setView([0, 0], this.defaultZoomLevel || 8);
var tileLayer = L.tileLayer.provider(mapProvider); var tileLayer = L.tileLayer.provider(mapProvider);
tileLayer.addTo(this.map); tileLayer.addTo(this.map);
if (initCallback) { if (initCallback) {
setTimeout(initCallback, 0); //eslint-disable-line setTimeout(initCallback, 0); //eslint-disable-line
} }
} }
inited() { inited() {
return angular.isDefined(this.map); return angular.isDefined(this.map);
} }
updateMarkerLabel(marker, settings) { updateMarkerLabel(marker, settings) {
marker.unbindTooltip(); marker.unbindTooltip();
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>', marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset }); {className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
} }
updateMarkerColor(marker, color) { updateMarkerColor(marker, color) {
this.createDefaultMarkerIcon(marker, color, (iconInfo) => { this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
marker.setIcon(iconInfo.icon); marker.setIcon(iconInfo.icon);
}); });
} }
updateMarkerIcon(marker, settings) { updateMarkerIcon(marker, settings) {
this.createMarkerIcon(marker, settings, (iconInfo) => { this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon); marker.setIcon(iconInfo.icon);
if (settings.showLabel) { if (settings.showLabel) {
marker.unbindTooltip(); marker.unbindTooltip();
marker.tooltipOffset = [0, -iconInfo.size[1] + 10]; marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>', marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset }); {className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
} }
}); });
} }
createMarkerIcon(marker, settings, onMarkerIconReady) { createMarkerIcon(marker, settings, onMarkerIconReady) {
var currentImage = settings.currentImage; var currentImage = settings.currentImage;
var opMap = this; var opMap = this;
if (currentImage && currentImage.url) { if (currentImage && currentImage.url) {
this.utils.loadImageAspect(currentImage.url).then( this.utils.loadImageAspect(currentImage.url).then(
(aspect) => { (aspect) => {
if (aspect) { if (aspect) {
var width; var width;
var height; var height;
if (aspect > 1) { if (aspect > 1) {
width = currentImage.size; width = currentImage.size;
height = currentImage.size / aspect; height = currentImage.size / aspect;
} else { } else {
width = currentImage.size * aspect; width = currentImage.size * aspect;
height = currentImage.size; height = currentImage.size;
} }
var icon = L.icon({ var icon = L.icon({
iconUrl: currentImage.url, iconUrl: currentImage.url,
iconSize: [width, height], iconSize: [width, height],
iconAnchor: [width/2, height], iconAnchor: [width / 2, height],
popupAnchor: [0, -height] popupAnchor: [0, -height]
}); });
var iconInfo = { var iconInfo = {
size: [width, height], size: [width, height],
icon: icon icon: icon
}; };
onMarkerIconReady(iconInfo); onMarkerIconReady(iconInfo);
} else { } else {
opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady); opMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
} }
} }
); );
} else { } else {
this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady); this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
} }
} }
createDefaultMarkerIcon(marker, color, onMarkerIconReady) { createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
var pinColor = color.substr(1); var pinColor = color.substr(1);
var icon = L.icon({ var icon = L.icon({
iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor, iconUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + pinColor,
iconSize: [21, 34], iconSize: [21, 34],
iconAnchor: [10, 34], iconAnchor: [10, 34],
popupAnchor: [0, -34], popupAnchor: [0, -34],
shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow', shadowUrl: 'https://chart.apis.google.com/chart?chst=d_map_pin_shadow',
shadowSize: [40, 37], shadowSize: [40, 37],
shadowAnchor: [12, 35] shadowAnchor: [12, 35]
}); });
var iconInfo = { var iconInfo = {
size: [21, 34], size: [21, 34],
icon: icon icon: icon
}; };
onMarkerIconReady(iconInfo); onMarkerIconReady(iconInfo);
} }
createMarker(location, dsIndex, settings, onClickListener, markerArgs) { createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
var marker = L.marker(location, {}); var marker = L.marker(location, {});
var opMap = this; var opMap = this;
this.createMarkerIcon(marker, settings, (iconInfo) => { this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.setIcon(iconInfo.icon); marker.setIcon(iconInfo.icon);
if (settings.showLabel) { if (settings.showLabel) {
marker.tooltipOffset = [0, -iconInfo.size[1] + 10]; marker.tooltipOffset = [0, -iconInfo.size[1] + 10];
marker.bindTooltip('<div style="color: '+ settings.labelColor +';"><b>'+settings.labelText+'</b></div>', marker.bindTooltip('<div style="color: ' + settings.labelColor + ';"><b>' + settings.labelText + '</b></div>',
{ className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset }); {className: 'tb-marker-label', permanent: true, direction: 'top', offset: marker.tooltipOffset});
} }
marker.addTo(opMap.map); marker.addTo(opMap.map);
}); });
if (settings.displayTooltip) { if (settings.displayTooltip) {
this.createTooltip(marker, dsIndex, settings, markerArgs); this.createTooltip(marker, dsIndex, settings, markerArgs);
} }
if (onClickListener) { if (onClickListener) {
marker.on('click', onClickListener); marker.on('click', onClickListener);
} }
return marker; return marker;
} }
removeMarker(marker) { removeMarker(marker) {
this.map.removeLayer(marker); this.map.removeLayer(marker);
} }
createTooltip(marker, dsIndex, settings, markerArgs) { createTooltip(marker, dsIndex, settings, markerArgs) {
var popup = L.popup(); var popup = L.popup();
popup.setContent(''); popup.setContent('');
marker.bindPopup(popup, {autoClose: settings.autocloseTooltip, closeOnClick: false}); marker.bindPopup(popup, {autoClose: settings.autocloseTooltip, closeOnClick: false});
this.tooltips.push( { this.tooltips.push({
markerArgs: markerArgs, markerArgs: markerArgs,
popup: popup, popup: popup,
locationSettings: settings, locationSettings: settings,
dsIndex: dsIndex dsIndex: dsIndex
}); });
} }
updatePolylineColor(polyline, settings, color) { updatePolylineColor(polyline, settings, color) {
var style = { var style = {
color: color, color: color,
opacity: settings.strokeOpacity, opacity: settings.strokeOpacity,
weight: settings.strokeWeight weight: settings.strokeWeight
}; };
polyline.setStyle(style); polyline.setStyle(style);
} }
createPolyline(locations, settings) { createPolyline(locations, settings) {
var polyline = L.polyline(locations, var polyline = L.polyline(locations,
{ {
color: settings.color, color: settings.color,
opacity: settings.strokeOpacity, opacity: settings.strokeOpacity,
weight: settings.strokeWeight weight: settings.strokeWeight
} }
).addTo(this.map); ).addTo(this.map);
return polyline; return polyline;
} }
removePolyline(polyline) { removePolyline(polyline) {
this.map.removeLayer(polyline); this.map.removeLayer(polyline);
} }
fitBounds(bounds) { createPolygon(latLangs, settings) {
if (bounds.isValid()) { let polygon = L.polygon(latLangs, {
if (this.dontFitMapBounds && this.defaultZoomLevel) { fill: true,
this.map.setZoom(this.defaultZoomLevel, {animate: false}); fillColor: settings.polygonColor,
this.map.panTo(bounds.getCenter(), {animate: false}); color: settings.polygonStrokeColor,
} else { weight: settings.polygonStrokeWeight,
var tbMap = this; fillOpacity: settings.polygonOpacity,
this.map.once('zoomend', function() { opacity: settings.polygonStrokeOpacity
if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { }).addTo(this.map);
tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false}); return polygon;
} }
});
this.map.fitBounds(bounds, {padding: [50, 50], animate: false}); removePolygon(polygon) {
} this.map.removeLayer(polygon);
} }
}
updatePolygonColor(polygon, settings, color) {
createLatLng(lat, lng) { let style = {
return L.latLng(lat, lng); fill: true,
} fillColor: color,
color: color,
extendBoundsWithMarker(bounds, marker) { weight: settings.polygonStrokeWeight,
bounds.extend(marker.getLatLng()); fillOpacity: settings.polygonOpacity,
} opacity: settings.polygonStrokeOpacity
};
getMarkerPosition(marker) { polygon.setStyle(style);
return marker.getLatLng(); }
}
getPolygonLatLngs(polygon) {
setMarkerPosition(marker, latLng) { return polygon.getLatLngs();
marker.setLatLng(latLng); }
}
setPolygonLatLngs(polygon, latLngs) {
getPolylineLatLngs(polyline) { polygon.setLatLngs(latLngs);
return polyline.getLatLngs(); }
}
fitBounds(bounds) {
setPolylineLatLngs(polyline, latLngs) { if (bounds.isValid()) {
polyline.setLatLngs(latLngs); if (this.dontFitMapBounds && this.defaultZoomLevel) {
} this.map.setZoom(this.defaultZoomLevel, {animate: false});
this.map.panTo(bounds.getCenter(), {animate: false});
createBounds() { } else {
return L.latLngBounds(); var tbMap = this;
} this.map.once('zoomend', function () {
if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
extendBounds(bounds, polyline) { tbMap.map.setZoom(tbMap.minZoomLevel, {animate: false});
if (polyline && polyline.getLatLngs()) { }
bounds.extend(polyline.getBounds()); });
} this.map.fitBounds(bounds, {padding: [50, 50], animate: false});
} }
}
invalidateSize() { }
this.map.invalidateSize(true);
} createLatLng(lat, lng) {
return L.latLng(lat, lng);
getTooltips() { }
return this.tooltips;
} extendBoundsWithMarker(bounds, marker) {
bounds.extend(marker.getLatLng());
}
getMarkerPosition(marker) {
return marker.getLatLng();
}
setMarkerPosition(marker, latLng) {
marker.setLatLng(latLng);
}
getPolylineLatLngs(polyline) {
return polyline.getLatLngs();
}
setPolylineLatLngs(polyline, latLngs) {
polyline.setLatLngs(latLngs);
}
createBounds() {
return L.latLngBounds();
}
extendBounds(bounds, polyline) {
if (polyline && polyline.getLatLngs()) {
bounds.extend(polyline.getBounds());
}
}
invalidateSize() {
this.map.invalidateSize(true);
}
getTooltips() {
return this.tooltips;
}
} }

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

@ -14,377 +14,425 @@
* limitations under the License. * limitations under the License.
*/ */
var tmGlobals = { var tmGlobals = {
loadingTmId: null, loadingTmId: null,
tmApiKeys: {} tmApiKeys: {}
} }
export default class TbTencentMap { export default class TbTencentMap {
constructor($containerElement,utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, tmApiKey, tmDefaultMapType) { constructor($containerElement, utils, initCallback, defaultZoomLevel, dontFitMapBounds, minZoomLevel, tmApiKey, tmDefaultMapType) {
var tbMap = this; var tbMap = this;
this.utils = utils; this.utils = utils;
this.defaultZoomLevel = defaultZoomLevel; this.defaultZoomLevel = defaultZoomLevel;
this.dontFitMapBounds = dontFitMapBounds; this.dontFitMapBounds = dontFitMapBounds;
this.minZoomLevel = minZoomLevel; this.minZoomLevel = minZoomLevel;
this.tooltips = []; this.tooltips = [];
this.defaultMapType = tmDefaultMapType; this.defaultMapType = tmDefaultMapType;
function clearGlobalId() { function clearGlobalId() {
if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) { if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
tmGlobals.loadingTmId = null; tmGlobals.loadingTmId = null;
} }
} }
function displayError(message) { function displayError(message) {
$containerElement.html( // eslint-disable-line angular/angularelement $containerElement.html( // eslint-disable-line angular/angularelement
"<div class='error'>"+ message + "</div>" "<div class='error'>" + message + "</div>"
); );
} }
function initTencentMap() { function initTencentMap() {
tbMap.map = new qq.maps.Map($containerElement[0], { // eslint-disable-line no-undef tbMap.map = new qq.maps.Map($containerElement[0], { // eslint-disable-line no-undef
scrollwheel: true, scrollwheel: true,
mapTypeId: getTencentMapTypeId(tbMap.defaultMapType), mapTypeId: getTencentMapTypeId(tbMap.defaultMapType),
zoom: tbMap.defaultZoomLevel || 8 zoom: tbMap.defaultZoomLevel || 8
}); });
if (initCallback) { if (initCallback) {
initCallback(); initCallback();
} }
} }
/* eslint-disable no-undef */ /* eslint-disable no-undef */
function getTencentMapTypeId(mapType) { function getTencentMapTypeId(mapType) {
var mapTypeId =qq.maps.MapTypeId.ROADMAP; var mapTypeId = qq.maps.MapTypeId.ROADMAP;
if (mapType) { if (mapType) {
if (mapType === 'hybrid') { if (mapType === 'hybrid') {
mapTypeId = qq.maps.MapTypeId.HYBRID; mapTypeId = qq.maps.MapTypeId.HYBRID;
} else if (mapType === 'satellite') { } else if (mapType === 'satellite') {
mapTypeId = qq.maps.MapTypeId.SATELLITE; mapTypeId = qq.maps.MapTypeId.SATELLITE;
} else if (mapType === 'terrain') { } else if (mapType === 'terrain') {
mapTypeId = qq.maps.MapTypeId.ROADMAP; mapTypeId = qq.maps.MapTypeId.ROADMAP;
} }
} }
return mapTypeId; return mapTypeId;
} }
/* eslint-enable no-undef */ /* eslint-enable no-undef */
this.mapId = '' + Math.random().toString(36).substr(2, 9); this.mapId = '' + Math.random().toString(36).substr(2, 9);
this.apiKey = tmApiKey || '84d6d83e0e51e481e50454ccbe8986b'; this.apiKey = tmApiKey || '84d6d83e0e51e481e50454ccbe8986b';
window.tm_authFailure = function() { // eslint-disable-line no-undef, angular/window-service window.tm_authFailure = function () { // eslint-disable-line no-undef, angular/window-service
if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) { if (tmGlobals.loadingTmId && tmGlobals.loadingTmId === tbMap.mapId) {
tmGlobals.loadingTmId = null; tmGlobals.loadingTmId = null;
tmGlobals.tmApiKeys[tbMap.apiKey].error = 'Unable to authentificate for tencent Map API.</br>Please check your API key.'; tmGlobals.tmApiKeys[tbMap.apiKey].error = 'Unable to authentificate for tencent Map API.</br>Please check your API key.';
displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error); displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
} }
}; };
this.initMapFunctionName = 'initTencentMap_' + this.mapId; this.initMapFunctionName = 'initTencentMap_' + this.mapId;
window[this.initMapFunctionName] = function() { // eslint-disable-line no-undef, angular/window-service window[this.initMapFunctionName] = function () { // eslint-disable-line no-undef, angular/window-service
tmGlobals.tmApiKeys[tbMap.apiKey].loaded = true; tmGlobals.tmApiKeys[tbMap.apiKey].loaded = true;
initTencentMap(); initTencentMap();
for (var p = 0; p < tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits.length; p++) { for (var p = 0; p < tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits.length; p++) {
var pendingInit = tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits[p]; var pendingInit = tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits[p];
pendingInit(); pendingInit();
} }
tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits = []; tmGlobals.tmApiKeys[tbMap.apiKey].pendingInits = [];
}; };
if (this.apiKey && this.apiKey.length > 0) { if (this.apiKey && this.apiKey.length > 0) {
if (tmGlobals.tmApiKeys[this.apiKey]) { if (tmGlobals.tmApiKeys[this.apiKey]) {
if (tmGlobals.tmApiKeys[this.apiKey].error) { if (tmGlobals.tmApiKeys[this.apiKey].error) {
displayError(tmGlobals.tmApiKeys[this.apiKey].error); displayError(tmGlobals.tmApiKeys[this.apiKey].error);
} else if (tmGlobals.tmApiKeys[this.apiKey].loaded) { } else if (tmGlobals.tmApiKeys[this.apiKey].loaded) {
initTencentMap(); initTencentMap();
} else { } else {
tmGlobals.tmApiKeys[this.apiKey].pendingInits.push(initTencentMap); tmGlobals.tmApiKeys[this.apiKey].pendingInits.push(initTencentMap);
} }
} else { } else {
tmGlobals.tmApiKeys[this.apiKey] = { tmGlobals.tmApiKeys[this.apiKey] = {
loaded: false, loaded: false,
pendingInits: [] pendingInits: []
}; };
var tencentMapScriptRes = 'https://map.qq.com/api/js?v=2.exp&key='+this.apiKey+'&callback='+this.initMapFunctionName; var tencentMapScriptRes = 'https://map.qq.com/api/js?v=2.exp&key=' + this.apiKey + '&callback=' + this.initMapFunctionName;
tmGlobals.loadingTmId = this.mapId; tmGlobals.loadingTmId = this.mapId;
lazyLoad.load({ type: 'js', path: tencentMapScriptRes }).then( // eslint-disable-line no-undef lazyLoad.load({type: 'js', path: tencentMapScriptRes}).then( // eslint-disable-line no-undef
function success() { function success() {
setTimeout(clearGlobalId, 2000); // eslint-disable-line no-undef, angular/timeout-service setTimeout(clearGlobalId, 2000); // eslint-disable-line no-undef, angular/timeout-service
}, },
function fail(e) { function fail(e) {
clearGlobalId(); clearGlobalId();
tmGlobals.tmApiKeys[tbMap.apiKey].error = 'tencent map api load failed!</br>'+e; tmGlobals.tmApiKeys[tbMap.apiKey].error = 'tencent map api load failed!</br>' + e;
displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error); displayError(tmGlobals.tmApiKeys[tbMap.apiKey].error);
} }
); );
} }
} else { } else {
displayError('No tencent Map Api Key provided!'); displayError('No tencent Map Api Key provided!');
} }
} }
inited() { inited() {
return angular.isDefined(this.map); return angular.isDefined(this.map);
} }
createMarkerLabelStyle(settings) { createMarkerLabelStyle(settings) {
return { return {
width: "200px", width: "200px",
textAlign: "center", textAlign: "center",
color: settings.labelColor, color: settings.labelColor,
background: "none", background: "none",
border: "none", border: "none",
fontSize: "12px", fontSize: "12px",
fontFamily: "\"Helvetica Neue\", Arial, Helvetica, sans-serif", fontFamily: "\"Helvetica Neue\", Arial, Helvetica, sans-serif",
fontWeight: "bold" fontWeight: "bold"
}; };
} }
/* eslint-disable no-undef,no-unused-vars*/ /* eslint-disable no-undef,no-unused-vars*/
updateMarkerLabel(marker, settings) { updateMarkerLabel(marker, settings) {
if (marker.label) { if (marker.label) {
marker.label.setContent(settings.labelText); marker.label.setContent(settings.labelText);
marker.label.setStyle(this.createMarkerLabelStyle(settings)); marker.label.setStyle(this.createMarkerLabelStyle(settings));
} }
} }
/* eslint-enable no-undef,no-unused-vars */
/* eslint-enable no-undef,no-unused-vars */
/* eslint-disable no-undef,no-unused-vars */
updateMarkerColor(marker, color) { /* eslint-disable no-undef,no-unused-vars */
this.createDefaultMarkerIcon(marker, color, (iconInfo) => { updateMarkerColor(marker, color) {
marker.setIcon(iconInfo.icon); this.createDefaultMarkerIcon(marker, color, (iconInfo) => {
}); marker.setIcon(iconInfo.icon);
} });
/* eslint-enable no-undef,,no-unused-vars */ }
/* eslint-disable no-undef */ /* eslint-enable no-undef,,no-unused-vars */
updateMarkerIcon(marker, settings) {
this.createMarkerIcon(marker, settings, (iconInfo) => { /* eslint-disable no-undef */
marker.setIcon(iconInfo.icon); updateMarkerIcon(marker, settings) {
if (marker.label) { this.createMarkerIcon(marker, settings, (iconInfo) => {
marker.label.setOffset(new qq.maps.Size(-100, -iconInfo.size[1]-20)); marker.setIcon(iconInfo.icon);
} if (marker.label) {
}); marker.label.setOffset(new qq.maps.Size(-100, -iconInfo.size[1] - 20));
} }
/* eslint-disable no-undef */ });
}
/* eslint-disable no-undef */
createMarkerIcon(marker, settings, onMarkerIconReady) { /* eslint-disable no-undef */
var currentImage = settings.currentImage;
var tMap = this; /* eslint-disable no-undef */
if (currentImage && currentImage.url) { createMarkerIcon(marker, settings, onMarkerIconReady) {
this.utils.loadImageAspect(currentImage.url).then( var currentImage = settings.currentImage;
(aspect) => { var tMap = this;
if (aspect) { if (currentImage && currentImage.url) {
var width; this.utils.loadImageAspect(currentImage.url).then(
var height; (aspect) => {
if (aspect > 1) { if (aspect) {
width = currentImage.size; var width;
height = currentImage.size / aspect; var height;
} else { if (aspect > 1) {
width = currentImage.size * aspect; width = currentImage.size;
height = currentImage.size; height = currentImage.size / aspect;
} } else {
var icon = new qq.maps.MarkerImage(currentImage.url, width = currentImage.size * aspect;
new qq.maps.Size(width, height), height = currentImage.size;
new qq.maps.Point(0,0), }
new qq.maps.Point(width/2, height), var icon = new qq.maps.MarkerImage(currentImage.url,
new qq.maps.Size(width, height)); new qq.maps.Size(width, height),
var iconInfo = { new qq.maps.Point(0, 0),
size: [width, height], new qq.maps.Point(width / 2, height),
icon: icon new qq.maps.Size(width, height));
}; var iconInfo = {
onMarkerIconReady(iconInfo); size: [width, height],
} else { icon: icon
tMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady); };
} onMarkerIconReady(iconInfo);
} } else {
); tMap.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
} else { }
this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady); }
} );
} } else {
/* eslint-enable no-undef */ this.createDefaultMarkerIcon(marker, settings.color, onMarkerIconReady);
}
/* eslint-disable no-undef */ }
createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
var pinColor = color.substr(1); /* eslint-enable no-undef */
var icon = new qq.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
new qq.maps.Size(40, 37), /* eslint-disable no-undef */
new qq.maps.Point(0,0), createDefaultMarkerIcon(marker, color, onMarkerIconReady) {
new qq.maps.Point(10, 37)); var pinColor = color.substr(1);
var iconInfo = { var icon = new qq.maps.MarkerImage("https://chart.apis.google.com/chart?chst=d_map_pin_letter_withshadow&chld=%E2%80%A2|" + pinColor,
size: [40, 37], new qq.maps.Size(40, 37),
icon: icon new qq.maps.Point(0, 0),
}; new qq.maps.Point(10, 37));
onMarkerIconReady(iconInfo); var iconInfo = {
} size: [40, 37],
/* eslint-enable no-undef */ icon: icon
};
/* eslint-disable no-undef */ onMarkerIconReady(iconInfo);
createMarker(location, dsIndex, settings, onClickListener, markerArgs) { }
var marker = new qq.maps.Marker({
position: location /* eslint-enable no-undef */
});
var tMap = this; /* eslint-disable no-undef */
this.createMarkerIcon(marker, settings, (iconInfo) => { createMarker(location, dsIndex, settings, onClickListener, markerArgs) {
marker.setIcon(iconInfo.icon); var marker = new qq.maps.Marker({
marker.setMap(tMap.map); position: location
if (settings.showLabel) { });
marker.label = new qq.maps.Label({ var tMap = this;
clickable: false, this.createMarkerIcon(marker, settings, (iconInfo) => {
content: settings.labelText, marker.setIcon(iconInfo.icon);
offset: new qq.maps.Size(-100, -iconInfo.size[1]-20), marker.setMap(tMap.map);
style: tMap.createMarkerLabelStyle(settings), if (settings.showLabel) {
visible: true, marker.label = new qq.maps.Label({
position: location, clickable: false,
map: tMap.map, content: settings.labelText,
zIndex: 1000 offset: new qq.maps.Size(-100, -iconInfo.size[1] - 20),
}); style: tMap.createMarkerLabelStyle(settings),
} visible: true,
}); position: location,
map: tMap.map,
if (settings.displayTooltip) { zIndex: 1000
this.createTooltip(marker, dsIndex, settings, markerArgs); });
} }
});
if (onClickListener) {
qq.maps.event.addListener(marker, 'click', onClickListener); if (settings.displayTooltip) {
} this.createTooltip(marker, dsIndex, settings, markerArgs);
}
return marker;
} if (onClickListener) {
qq.maps.event.addListener(marker, 'click', onClickListener);
/* eslint-disable no-undef */ }
removeMarker(marker) {
marker.setMap(null); return marker;
if (marker.label) { }
marker.label.setMap(null);
} /* eslint-disable no-undef */
} removeMarker(marker) {
marker.setMap(null);
/* eslint-enable no-undef */ if (marker.label) {
marker.label.setMap(null);
/* eslint-disable no-undef */ }
createTooltip(marker, dsIndex, settings, markerArgs) { }
var popup = new qq.maps.InfoWindow({
map :this.map /* eslint-enable no-undef */
});
var map = this; /* eslint-disable no-undef */
qq.maps.event.addListener(marker, 'click', function() { createTooltip(marker, dsIndex, settings, markerArgs) {
if (settings.autocloseTooltip) { var popup = new qq.maps.InfoWindow({
map.tooltips.forEach((tooltip) => { map: this.map
tooltip.popup.close(); });
}); var map = this;
} qq.maps.event.addListener(marker, 'click', function () {
popup.open(); if (settings.autocloseTooltip) {
popup.setPosition(marker); map.tooltips.forEach((tooltip) => {
}); tooltip.popup.close();
this.tooltips.push( { });
markerArgs: markerArgs, }
popup: popup, popup.open();
locationSettings: settings, popup.setPosition(marker);
dsIndex: dsIndex });
}); this.tooltips.push({
} markerArgs: markerArgs,
/* eslint-enable no-undef */ popup: popup,
locationSettings: settings,
/* eslint-disable no-undef */ dsIndex: dsIndex
updatePolylineColor(polyline, settings, color) { });
var options = { }
path: polyline.getPath(),
strokeColor: color, /* eslint-enable no-undef */
strokeOpacity: settings.strokeOpacity,
strokeWeight: settings.strokeWeight, /* eslint-disable no-undef */
map: this.map updatePolylineColor(polyline, settings, color) {
}; var options = {
polyline.setOptions(options); path: polyline.getPath(),
} strokeColor: color,
/* eslint-enable no-undef */ strokeOpacity: settings.strokeOpacity,
strokeWeight: settings.strokeWeight,
/* eslint-disable no-undef */ map: this.map
createPolyline(locations, settings) { };
var polyline = new qq.maps.Polyline({ polyline.setOptions(options);
path: locations, }
strokeColor: settings.color,
strokeOpacity: settings.strokeOpacity, /* eslint-enable no-undef */
strokeWeight: settings.strokeWeight,
map: this.map /* eslint-disable no-undef */
}); createPolyline(locations, settings) {
var polyline = new qq.maps.Polyline({
return polyline; path: locations,
} strokeColor: settings.color,
/* eslint-enable no-undef */ strokeOpacity: settings.strokeOpacity,
strokeWeight: settings.strokeWeight,
removePolyline(polyline) { map: this.map
polyline.setMap(null); });
}
return polyline;
/* eslint-disable no-undef ,no-unused-vars*/ }
fitBounds(bounds) {
if (this.dontFitMapBounds && this.defaultZoomLevel) { /* eslint-enable no-undef */
this.map.setZoom(this.defaultZoomLevel);
this.map.setCenter(bounds.getCenter()); removePolyline(polyline) {
} else { polyline.setMap(null);
var tbMap = this; }
qq.maps.event.addListenerOnce(this.map, 'bounds_changed', function() { // eslint-disable-line no-undef
if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) { /* eslint-disable no-undef */
tbMap.map.setZoom(tbMap.minZoomLevel); createPolygon(latLangs, settings) {
} let polygon = new qq.maps.Polygon({
}); map: this.map,
this.map.fitBounds(bounds); path: latLangs,
} strokeColor: settings.polygonStrokeColor,
} fillColor: settings.polygonColor,
/* eslint-enable no-undef,no-unused-vars */ strokeWeight: settings.polygonStrokeWeight
});
createLatLng(lat, lng) { return polygon;
return new qq.maps.LatLng(lat, lng); // eslint-disable-line no-undef }
} /* eslint-disable no-undef */
extendBoundsWithMarker(bounds, marker) { removePolygon (polygon) {
bounds.extend(marker.getPosition()); polygon.setMap(null);
} }
getMarkerPosition(marker) { /* eslint-disable no-undef,no-unused-vars */
return marker.getPosition(); updatePolygonColor (polygon, settings, color) {
} let options = {
path: polygon.getPath(),
setMarkerPosition(marker, latLng) { map: this.map,
marker.setPosition(latLng); strokeColor: color,
if (marker.label) { fillColor: color,
marker.label.setPosition(latLng); strokeWeight: settings.polygonStrokeWeight
} }
}
}
getPolylineLatLngs(polyline) { /* eslint-disable no-undef ,no-unused-vars*/
return polyline.getPath().getArray();
}
getPolygonLatLngs(polygon) {
setPolylineLatLngs(polyline, latLngs) { return polygon.getPath().getArray();
polyline.setPath(latLngs); }
}
setPolygonLatLngs(polygon, latLngs) {
createBounds() { polygon.setPath(latLngs);
return new qq.maps.LatLngBounds(); // eslint-disable-line no-undef }
}
/* eslint-disable no-undef ,no-unused-vars*/
extendBounds(bounds, polyline) { fitBounds(bounds) {
if (polyline && polyline.getPath()) { if (this.dontFitMapBounds && this.defaultZoomLevel) {
var locations = polyline.getPath(); this.map.setZoom(this.defaultZoomLevel);
for (var i = 0; i < locations.getLength(); i++) { this.map.setCenter(bounds.getCenter());
bounds.extend(locations.getAt(i)); } else {
} var tbMap = this;
} qq.maps.event.addListenerOnce(this.map, 'bounds_changed', function () { // eslint-disable-line no-undef
} if (!tbMap.defaultZoomLevel && tbMap.map.getZoom() > tbMap.minZoomLevel) {
tbMap.map.setZoom(tbMap.minZoomLevel);
invalidateSize() { }
qq.maps.event.trigger(this.map, "resize"); // eslint-disable-line no-undef });
} this.map.fitBounds(bounds);
}
getTooltips() { }
return this.tooltips;
} /* eslint-enable no-undef,no-unused-vars */
createLatLng(lat, lng) {
return new qq.maps.LatLng(lat, lng); // eslint-disable-line no-undef
}
extendBoundsWithMarker(bounds, marker) {
bounds.extend(marker.getPosition());
}
getMarkerPosition(marker) {
return marker.getPosition();
}
setMarkerPosition(marker, latLng) {
marker.setPosition(latLng);
if (marker.label) {
marker.label.setPosition(latLng);
}
}
getPolylineLatLngs(polyline) {
return polyline.getPath().getArray();
}
setPolylineLatLngs(polyline, latLngs) {
polyline.setPath(latLngs);
}
createBounds() {
return new qq.maps.LatLngBounds(); // eslint-disable-line no-undef
}
extendBounds(bounds, polyline) {
if (polyline && polyline.getPath()) {
var locations = polyline.getPath();
for (var i = 0; i < locations.getLength(); i++) {
bounds.extend(locations.getAt(i));
}
}
}
invalidateSize() {
qq.maps.event.trigger(this.map, "resize"); // eslint-disable-line no-undef
}
getTooltips() {
return this.tooltips;
}
} }

Loading…
Cancel
Save