#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <cfloat>
#include <stack>
#include <queue>
#include <vector>
#include <string>
#include <iostream>
#include <set>
#include <map>
#include <time.h>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;


#define NUM 210

struct Info{
	double x,y,speed;
};

struct Data{
	Data(double arg_time,int arg_index){
		time = arg_time;
		index = arg_index;
	}
	bool operator<(const struct Data &arg) const{
		return time < arg.time;
	}
	double time;
	int index;
};

int N,M;
int V; //頂点数
vector<int> G[NUM]; //グラフの隣接リスト表現
int match[NUM]; //マッチングのペア
bool used[NUM]; //DFSですでに調べたかのフラグ
Info troops[100],bases[100];
vector<Data> DIST[100];


//fromとtoを結ぶ辺をグラフに追加する
void add_edge(int from,int to){
	G[from].push_back(to);
	G[to].push_back(from);
}

//増加パスをDFSで探す(node_idのペアを探す)
int dfs(int node_id){
	used[node_id] = true;

	for(int i = 0; i < G[node_id].size(); i++){
		int adj_node_id = G[node_id][i],pair_id = match[adj_node_id];
		if((pair_id < 0)|| //隣接するノードに、まだペアが存在しない
				(used[pair_id] == false && dfs(pair_id) == true)){

			match[node_id] = adj_node_id;
			match[adj_node_id] = node_id;
			return true;
		}
	}
	return false;
}


int bipartie_matching(){
	int ret = 0;
	for(int i = 0; i < V; i++)match[i] = -1; //ペアなしにしておく
	for(int node_id = 0; node_id < V; node_id++){
		if(match[node_id] < 0){ //node_idにペアがいない場合
			for(int i = 0; i < V; i++)used[i] = false;
			if(dfs(node_id)){ //増加パスが存在する場合
				ret++;
			}
		}
	}
	return ret;
}

bool is_OK(double time){
	for(int i = 0; i < NUM; i++)G[i].clear();

	int left,right,m,max_index;


	//troopから、time内に到達できる基地に辺を張る
	for(int i = 0; i < N; i++){
		left = 0,right = M-1,m = (left+right)/2;
		max_index = -1;

		while(left <= right){
			if(DIST[i][m].time <= time){
				max_index = m;
				left = m+1; //より右へ
			}else{
				right = m-1; //より左へ
			}
			m = (left+right)/2;
		}

		if(max_index == -1)continue; //到達できる基地がない

		for(int k = 0; k <= max_index; k++){
			add_edge(i,N+DIST[i][k].index);
		}
	}

	return  bipartie_matching() == M;
}

double calc_time(Info a,Info b){
	return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y))/a.speed;
}


void func(){

	for(int i = 0; i < 100; i++)DIST[i].clear();

	V = N+M;

	for(int i = 0; i < N; i++){
		scanf("%lf %lf %lf",&troops[i].x,&troops[i].y,&troops[i].speed);
	}

	for(int i = 0; i < M; i++){
		scanf("%lf %lf",&bases[i].x,&bases[i].y);
	}

	double tmp_time;

	//troopとbaseの時間距離を求める
	for(int i = 0; i < N; i++){
		for(int k = 0; k < M; k++){
			tmp_time = calc_time(troops[i],bases[k]);
			DIST[i].push_back(Data(tmp_time,k));
		}
		sort(DIST[i].begin(),DIST[i].end());
	}

	double ans = 99999999999.0;
	double left = 0.0,right = ans,m = (left+right)/2;

	while(right-left > EPS){
		if(is_OK(m)){
			ans = m;
			right = m-EPS;
		}else{
			left = m+EPS;
		}
		m = (left+right)/2;
	}

	printf("%.20lf\n",ans);
}

int main(){

	while(true){
		scanf("%d %d",&N,&M);
		if(N == 0 && M == 0)break;

		func();
	}

	return 0;
}

